Python建立多執行緒任務並獲取每個執行緒返回值

Candy_GL發表於2018-09-29

轉自:https://www.cnblogs.com/bethansy/p/7965820.html

1.程式和執行緒

    (1)程式是一個執行中的程式。每個程式都擁有自己的地址空間、記憶體、資料棧以及其他用於跟蹤執行的輔助資料。程式也可以派生新的程式來執行其他任務,不過每個新程式都擁有自己的記憶體和資料棧,所以只能採用程式間通訊(IPC)的方式共享資訊。

    (2)執行緒與程式類似,不過他們是在同一個程式下執行的,並共享相同的上下文。執行緒一般是以併發方式執行的,但是在單核CPU中真正的併發是不可能的,:每個執行緒執行一小會兒,然後讓步給其他線(再次排隊等待更多的CPU時間)。但是,多執行緒訪問同一片資料,由於訪問的順序不同可能導致結構不一致。例如append(0-1)和print會同時有01

PS:記憶體中可以有許多程式,但是在任意給定時刻只能有一個程式在執行。同理,儘管Python 直譯器中可以執行多個執行緒,但是在任意給定時刻只有一個執行緒會被直譯器執行。

2.執行緒

    執行緒相關的模組有thread和threading,其中threading是thread的改進和升級版,且thread模組有一個致命的缺點,在主執行緒退出之後,所有其他執行緒都會在沒有清理的情況下直接退出。threading模組中加入守護執行緒概念,如果被指明為守護執行緒後主執行緒退出後不會等待守護執行緒執行完畢才吐出。整個Python 程式(可以解讀為:主執行緒)將在所有非守護執行緒退出之後才退出,換句話說,就是沒有剩下存活的非守護執行緒時。

主執行緒和子執行緒分別是什麼?舉例?

     而主執行緒應該做一個好的管理者,負責瞭解每個單獨的執行緒需要執行什麼,每個派生的執行緒需要哪些資料或引數,這些執行緒執行完成後會提供什麼結果。這樣,主執行緒就可以收集每個執行緒的結果,然後彙總成一個有意義的最終結果。

(1)單執行緒:

  我需要做兩件事,只能做完一件再做第二件,排好隊

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

# -*-coding:utf-8-*-

from time import ctime, sleep

import threading

loops = [42]

 

 

def loop(nloop,nsec):

    print('start loop', nloop, 'at :', ctime())

    sleep(nsec)

    print('done loop', nloop, 'at:', ctime())

 

 

def main():

    print('start at',ctime())

    nloops = range(len(loops))

    for in nloops:

        loop(i, loops[i])

 

    print('DONE AT:', ctime())

if __name__ == '__main__':

    main()

 返回結果:

start at Sun Dec 3 12:10:52 2017
start loop 0 at : Sun Dec 3 12:10:52 2017
done loop 0 at: Sun Dec 3 12:10:56 2017
start loop 1 at : Sun Dec 3 12:10:56 2017
done loop 1 at: Sun Dec 3 12:10:58 2017
DONE AT: Sun Dec 3 12:10:58 2017

(2) 多執行緒:(建立threads例項,傳給他一個函式)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

# -*-coding:utf-8-*-

from time import ctime, sleep

import threading

loops = [42]

 

 

def loop(nloop,nsec):

    print('start loop', nloop, 'at :', ctime())

    sleep(nsec)

    print('done loop', nloop, 'at:', ctime())

 

 

def main():

    print('start at',ctime())

    threads = []

    nloops = range(len(loops))

    for in nloops:

        = threading.Thread(target=loop,args=(i,loops[i]))

        threads.append(t)

    for in nloops:   # start threads 此處並不會執行執行緒,而是將任務分發到每個執行緒,同步執行緒。等同步完成後再開始執行start方法

        threads[i].start()

    for in nloops:   # jion()方法等待執行緒完成

        threads[i].jion()

    print('DONE AT:', ctime())

if __name__ == '__main__':

    main()

執行結果:

start at Sun Dec 3 12:08:23 2017
start loop 0 at : Sun Dec 3 12:08:23 2017
start loop 1 at : Sun Dec 3 12:08:23 2017
done loop 1 at: Sun Dec 3 12:08:25 2017
done loop 0 at: Sun Dec 3 12:08:27 2017
DONE AT: Sun Dec 3 12:08:27 2017

可以看到loop0和loop1同時進行,當時間較長的子執行緒loop0完成後,主執行緒print('DONE AT:', ctime())開始任務

(3)多執行緒(建立threads例項,傳遞給他一個可呼叫的類例項):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

# -*-coding:utf-8-*-

from time import ctime, sleep

import threading

loops = [42]

 

 

class MyThread(object):

    def __init__(self, func, args, name=''):

        self.name = name

        self.func = func

        self.args = args

 

    def __call__(self):

        self.func(*self.args)

 

 

def loop(nloop, nsec):

    print('start loop', nloop, 'at :', ctime())

    sleep(nsec)

    print('done loop', nloop, 'at:', ctime())

 

 

def main():

    print('start at',ctime())

    threads = []

    nloops = range(len(loops))

    for in nloops:

        = threading.Thread(target=MyThread(loop, (i, loops[i]), loop.__name__))

        threads.append(t)

    for in nloops:   # start threads 此處並不會執行執行緒,而是將任務分發到每個執行緒,同步執行緒。等同步完成後再開始執行start方法

        threads[i].start()

    for in nloops:   # jion()方法等待執行緒完成

        threads[i].join()

    print('DONE AT:', ctime())

if __name__ == '__main__':

    main()

join函式的原理就是一次檢驗執行緒池中的執行緒是否結束,沒有結束就阻塞直到執行緒結束。如果結束則就跳轉執行下一個執行緒的join函式。如果不用join(),主執行緒跑的比子執行緒快會拿不到結果

(3)通過多執行緒獲取返回值

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

# -*-coding:utf-8-*-

from time import ctime, sleep

import threading

import numpy as np

import collections

 

loops = ['廣州''北京']

t_list = ['01''02''03']

cldas_sum = collections.deque()

 

 

class MyThread(threading.Thread):

    def __init__(self, func, args, name=''):

        threading.Thread.__init__(self)

        self.name = name

        self.func = func

        self.args = args

        self.result = self.func(*self.args)

 

    def get_result(self):

        try:

            return self.result

        except Exception:

            return None

 

 

def loop(nloop):

    for in t_list:

        cldas_values = []

        for in range(4):

            cldas_value = nloop + str(k)

            cldas_values.append(cldas_value)

        cldas_values.append(j)

        cldas_values.append(nloop)

        cldas_sum.append(cldas_values)

        print(id(cldas_values))

    #print(cldas_sum)

    return cldas_sum

 

 

def main():

    print('start at', ctime())

    threads = []

    nloops = range(len(loops))

    for in nloops:

        = MyThread(loop, (loops[i],), loop.__name__)

        threads.append(t)

    for in nloops:   # start threads 此處並不會執行執行緒,而是將任務分發到每個執行緒,同步執行緒。等同步完成後再開始執行start方法

        threads[i].start()

    for in nloops:   # jion()方法等待執行緒完成

        threads[i].join()

    print(threads[1].get_result())

    print('DONE AT:', ctime())

 

 

if __name__ == '__main__':

    main()

 

  寫這個指令碼的一個目的就是看所有指令碼共同呼叫同一個函式,每個指令碼都在這個函式中都有一個相同的變數,那麼這個變數會被共用還是每個執行緒自己各有一個閉包。不過函式內定義的變數是閉包,呼叫函式時建立,返回時銷燬。

最終返回結果如下:

start at Tue Dec 5 10:32:38 2017
728072411976
728072411848
728072411784
728072411656
728072364680
728072364808
deque([['廣州0', '廣州1', '廣州2', '廣州3', '01', '廣州'], ['廣州0', '廣州1', '廣州2', '廣州3', '02', '廣州'], ['廣州0', '廣州1', '廣州2', '廣州3', '03', '廣州'], ['北京0', '北京1', '北京2', '北京3', '01', '北京'], ['北京0', '北京1', '北京2', '北京3', '02', '北京'], ['北京0', '北京1', '北京2', '北京3', '03', '北京']])
DONE AT: Tue Dec 5 10:32:38 2017

 需要注意的是:

(1)如果多個執行緒共用一個公共資料,那麼我們需要做的就是將這個公共資料設定成佇列格式,要不然多個執行緒共同訪問這個資料可能會出錯,需要加鎖。設定成佇列比加鎖再放鎖效率高多了

(2)執行緒之間同一個變數id都不一樣,還是不知道是否其他執行緒會涉足另一個執行緒

相關文章