1. 程式人生 > >join和 Daemon守護線程

join和 Daemon守護線程

process for循環 == ces 都是 啟動 thread exit 獨立

一、前言

  一個程序至少有一個主線程,主線程啟動子線程後,它們之間並沒有隸屬關系。主線程和子線程執行是並行的,相互獨立。主線程執行完畢後默認不等子線程執行結束就接著往下走了,如果有其他程序就會運行另外的程序,如果沒有就等待子線程執行完成後結束程序。

import threading
import time
import random


class MyThread(threading.Thread):

    def __init__(self, n):
        super(MyThread, self).__init__()
        self.n = n

    def run(self):
        print(‘task %s is operating‘ % self.n)
        t_time = random.randint(1, 8)
        time.sleep(t_time)
       # getNname獲取線程名稱
        print(self.getName(), ‘finished‘, ‘I sleep %d seconds‘ % t_time)


class Person(object):

    def __init__(self, name):
        self.name = name

    def get_info(self):
        # time.sleep(10)
        print(‘my name is %s.‘ % self.name)

if __name__ == ‘__main__‘:

    start_time = time.time()
    for i in range(5):
        t = MyThread(i)
        t.start()
    # t.join()
    print(‘main thread finished.‘)
    print(‘*****執行另一個程序了******‘)
    p = Person(‘bigberg‘)
    p.get_info()
    print(‘*************************‘)
    print(‘cost: %s‘ % (time.time() - start_time))

  結果:

# 線程開始執行

task 0 is operating
task 1 is operating
task 2 is operating
task 3 is operating
task 4 is operating
# 主線程執行完畢
main thread finished.

# 可以執行另一個程序了
*****執行另一個程序了******
my name is bigberg.
*************************
# 這裏花費的時間是主線程的運行時間,顯然沒有計算子線程的時間
cost: 0.0019881725311279297

# 子線程在主線程結束後依然在運行
Thread-3 finished I sleep 2 seconds
Thread-5 finished I sleep 2 seconds
Thread-2 finished I sleep 3 seconds
Thread-4 finished I sleep 4 seconds
Thread-1 finished I sleep 5 seconds

# 所有子線程完畢後程序結束
Process finished with exit code 0

  

二、join 等待子線程完成

  如果在線程實例後加上join默認主線程是阻塞的,主線程會等待該子線程運行完成後在結束。

# -*- coding: UTF-8 -*-

import threading
import time
import random


class MyThread(threading.Thread):

    def __init__(self, n):
        super(MyThread, self).__init__()
        self.n = n

    def run(self):
        print(‘task %s is operating‘ % self.n)
        t_time = random.randint(1, 8)
        time.sleep(t_time)
        print(self.getName(), ‘finished‘, ‘I sleep %d seconds‘ % t_time)


if __name__ == ‘__main__‘:

    start_time = time.time()
    for i in range(5):
        t = MyThread(i)
        t.start()
        t.join()    # 添加join,阻塞主線程
    print(‘main thread finished.‘)
    print(‘cost: %s‘ % (time.time() - start_time))

# 註
# 如果對每個線程都加join,那麽並發就沒有了,實際上線程都是串行的
# 前一個線程執行完了,才會執行下一個線程
# 主線程最後運行完畢

  結果:

task 0 is operating
Thread-1 finished I sleep 2 seconds
task 1 is operating
Thread-2 finished I sleep 6 seconds
task 2 is operating
Thread-3 finished I sleep 4 seconds
task 3 is operating
Thread-4 finished I sleep 8 seconds
task 4 is operating
Thread-5 finished I sleep 5 seconds
# 這裏主線程已經是最後執行完畢的了
main thread finished.

# 消耗的時間也是每個線程的運行時間之和
cost: 25.005265712738037

  2.1 計算並發運行時間

  如果不想計算出總的運行時間,而是所有線程的並發運行時間呢?就像上例中的那樣,最長運行時間是8秒,那麽所有線程都能在8秒內全部運行完畢。

  把t.join()單獨移到for循環外面是不行的,因為這樣並發運行總會在最後一個線程出阻塞。如下:  

# -*- coding: UTF-8 -*-

import threading
import time
import random


class MyThread(threading.Thread):

    def __init__(self, n):
        super(MyThread, self).__init__()
        self.n = n

    def run(self):
        print(‘task %s is operating‘ % self.n)
        t_time = random.randint(1, 8)
        time.sleep(t_time)
        print(self.getName(), ‘finished‘, ‘I sleep %d seconds‘ % t_time)


if __name__ == ‘__main__‘:

    start_time = time.time()
    for i in range(5):
        t = MyThread(i)
        t.start()
    t.join()    # 添加join,阻塞主線程
    print(‘main thread finished.‘)
    print(‘cost: %s‘ % (time.time() - start_time))

  結果:

task 0 is operating
task 1 is operating
task 2 is operating
task 3 is operating
task 4 is operating
Thread-1 finished I sleep 2 seconds
Thread-3 finished I sleep 2 seconds
Thread-5 finished I sleep 3 seconds
# 其實是在線程5,也就是最後一個線程出阻塞的
main thread finished.
cost: 3.001293659210205
Thread-2 finished I sleep 4 seconds
Thread-4 finished I sleep 5 seconds

  正確的方法,定義一個空列表,獲取所以的線程實例,for 循環阻塞所有的線程實例 

# -*- coding: UTF-8 -*-

import threading
import time
import random


class MyThread(threading.Thread):

    def __init__(self, n):
        super(MyThread, self).__init__()
        self.n = n

    def run(self):
        print(‘task %s is operating‘ % self.n)
        t_time = random.randint(1, 8)
        time.sleep(t_time)
        print(self.getName(), ‘finished‘, ‘I sleep %d seconds‘ % t_time)


if __name__ == ‘__main__‘:
    t_list = []
    start_time = time.time()
    for i in range(5):
        t = MyThread(i)
        t.start()
        t_list.append(t)

    for t in t_list:
        t.join()
    print(‘main thread finished.‘)
    print(‘cost: %s‘ % (time.time() - start_time))

  結果,事實上也符合我們剛才的推論,運行時間最長的線程所消耗的時間,就是總的並發時間 

task 0 is operating
task 1 is operating
task 2 is operating
task 3 is operating
task 4 is operating
Thread-3 finished I sleep 3 seconds
Thread-5 finished I sleep 3 seconds
Thread-2 finished I sleep 7 seconds
Thread-1 finished I sleep 7 seconds
Thread-4 finished I sleep 8 seconds
main thread finished.
cost: 8.001787185668945

# 並發時間在8秒左右

  總結:主線程創建一個子線程後,如果子線程調用join()方法,主線程會在調用的地方等待,直到該子線程運行完成才會接著往下執行。

三、守護線程setDaemon

  setDaemon()方法:在主線程中創建子線程,該子線程調用setDaemon方法後成為主線程的守護線程。這種情況下如果主線程執行結束,那麽不管子線程是否完成,一並和主線程退出。這裏基本和join()方法相反。此外,還有個要特別註意的:必須在start() 方法調用之前設置,如果不設置為守護線程,程序會被無限掛起。  

# -*- coding: UTF-8 -*-

import threading
import time
import random


class MyThread(threading.Thread):

    def __init__(self, n):
        super(MyThread, self).__init__()
        self.n = n

    def run(self):
        print(‘task %s is operating‘ % self.n)
        t_time = random.randint(1, 8)
        time.sleep(t_time)
        print(self.getName(), ‘finished‘, ‘I sleep %d seconds‘ % t_time)


if __name__ == ‘__main__‘:

    start_time = time.time()
    for i in range(5):
        t = MyThread(i)
        t.setDaemon(True)
        t.start()
    print(‘main thread finished.‘, threading.current_thread(), threading.active_count())
    print(‘cost: %s‘ % (time.time() - start_time))

  註:threading.current_thread()查看當前運行的線程

    threading.active_count() 查看活躍線程數

    線程數 = 主線程 + 子線程數

  結果: 

task 0 is operating
task 1 is operating
task 2 is operating
task 3 is operating
task 4 is operating
main thread finished. <_MainThread(MainThread, started 8656)> 6
cost: 0.0009999275207519531

Process finished with exit code 0

# 很顯然把子線程設置為主線程的守護線程後,主線程一旦結束,程序就執行退出運行,不會再等待子線程運行。

  註:如果程序中有其他非守護線程時,還是會等待非守護線程運行完畢,程序才會結束。

join和 Daemon守護線程