本文研究的主要是Python多線程threading和multiprocessing模塊的相關(guān)內(nèi)容,具體介紹如下。
線程是一個(gè)進(jìn)程的實(shí)體,是由表示程序運(yùn)行狀態(tài)的寄存器(如程序計(jì)數(shù)器、棧指針)以及堆棧組成,它是比進(jìn)程更小的單位。
線程是程序中的一個(gè)執(zhí)行流。一個(gè)執(zhí)行流是由CPU運(yùn)行程序代碼并操作程序的數(shù)據(jù)所形成的。因此,線程被認(rèn)為是以CPU為主體的行為。
線程不包含進(jìn)程地址空間中的代碼和數(shù)據(jù),線程是計(jì)算過程在某一時(shí)刻的狀態(tài)。所以,系統(tǒng)在產(chǎn)生一個(gè)線程或各個(gè)線程之間切換時(shí),負(fù)擔(dān)要比進(jìn)程小得多。
線程是一個(gè)用戶級(jí)的實(shí)體,線程結(jié)構(gòu)駐留在用戶空間中,能夠被普通的用戶級(jí)函數(shù)直接訪問。
一個(gè)線程本身不是程序,它必須運(yùn)行于一個(gè)程序(進(jìn)程)之中。因此,線程可以定義為一個(gè)程序中的單個(gè)執(zhí)行流。
多線程是指一個(gè)程序中包含多個(gè)執(zhí)行流,多線程是實(shí)現(xiàn)并發(fā)的一種有效手段。一個(gè)進(jìn)程在其執(zhí)行過程中,可以產(chǎn)生多個(gè)線程,形成多個(gè)執(zhí)行流。每個(gè)執(zhí)行流即每個(gè)線程也有它自身的產(chǎn)生、存在和消亡的過程。
多線程程序設(shè)計(jì)的含義就是可以將程序任務(wù)分成幾個(gè)并行的子任務(wù)。
線程的狀態(tài)圖:
Python中常使用的線程模塊
- thread(低版本使用的),threading
- Queue
- multiprocessing
threading
thread模塊是Python低版本中使用的,高版本中被threading代替了。threading模塊提供了更方便的API來操作線程。
threading.Thread
Thread是threading模塊中最重要的類之一,可以使用它來創(chuàng)建線程。創(chuàng)建新的線程有兩種方法:
- 方法一:直接創(chuàng)建threading.Thread類的對(duì)象,初始化時(shí)將可調(diào)用對(duì)象作為參數(shù)傳入。
- 方法二:通過繼承Thread類,重寫它的run方法。
Thread類的構(gòu)造方法:
1
|
__init__(group = None , target = None , name = None , args = (), kwargs = None , verbose = None ) |
參數(shù)說明:
group:線程組,目前還沒有實(shí)現(xiàn),庫引用中提示必須是None。
target:要執(zhí)行的方法;
name:線程名;
args/kwargs:要傳入方法的參數(shù)。
Thread類擁有的實(shí)例方法:
isAlive():返回線程是否在運(yùn)行。正在運(yùn)行指的是啟動(dòng)后,終止前。
getName(name)/setName(name):獲取/設(shè)置線程名。
isDaemon(bool)/setDaemon(bool):獲取/設(shè)置是否為守護(hù)線程。初始值從創(chuàng)建該線程的線程繼承而來,當(dāng)沒有非守護(hù)線程仍在運(yùn)行時(shí),程序?qū)⒔K止。
start():啟動(dòng)線程。
join([timeout]):阻塞當(dāng)前上下文環(huán)境的線程,直到調(diào)用此方法的線程終止或到達(dá)指定的等待時(shí)間timeout(可選參數(shù))。即當(dāng)前的線程要等調(diào)用join()這個(gè)方法的線程執(zhí)行完,或者是達(dá)到規(guī)定的時(shí)間。
直接創(chuàng)建threading.Thread類的對(duì)象
實(shí)例:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
from threading import Thread import time def run(a = None , b = None ) : print a, b time.sleep( 1 ) t = Thread(target = run, args = ( "this is a" , "thread" )) #此時(shí)線程是新建狀態(tài) print t.getName() #獲得線程對(duì)象名稱 print t.isAlive() #判斷線程是否還活著。 t.start() #啟動(dòng)線程 t.join() #等待其他線程運(yùn)行結(jié)束 |
執(zhí)行結(jié)果:
1
2
3
|
Thread - 1 False this is a thread |
注意:
1
|
t = Thread(target = run, args = ( "this is a" , "thread" )) |
這句只是創(chuàng)建了一個(gè)線程,并未執(zhí)行這個(gè)線程,此時(shí)線程處于新建狀態(tài)。
1
|
t.start() #啟動(dòng)線程 |
啟動(dòng)線程,此時(shí)線程扔為運(yùn)行,只是處于準(zhǔn)備狀態(tài)。
自定義函數(shù)run(),使我們自己根據(jù)我們需求自己定義的,函數(shù)名可以隨便取,run函數(shù)的參數(shù)來源于后面的args元組。
通過繼承Thread類
實(shí)例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
from threading import Thread import time class MyThread(Thread) : def __init__( self , a) : super (MyThread, self ).__init__() #調(diào)用父類的構(gòu)造方法 self .a = a def run( self ) : print "sleep :" , self .a time.sleep( self .a) t1 = MyThread( 2 ) t2 = MyThread( 4 ) t1.start() t2.start() t1.join() t2.join() |
執(zhí)行結(jié)果:
由于創(chuàng)建了兩個(gè)并發(fā)執(zhí)行的線程t1和t2,并發(fā)線程的執(zhí)行時(shí)間不定,誰先執(zhí)行完的時(shí)間也不定,所以執(zhí)行后打印的結(jié)果順序也是不定的。每一次執(zhí)行都有可能出現(xiàn)不同的結(jié)果。
注意:
繼承Thread類的新類MyThread構(gòu)造函數(shù)中必須要調(diào)用父類的構(gòu)造方法,這樣才能產(chǎn)生父類的構(gòu)造函數(shù)中的參數(shù),才能產(chǎn)生線程所需要的參數(shù)。新的類中如果需要?jiǎng)e的參數(shù),直接在其構(gòu)造方法中加即可。
同時(shí),新類中,在重寫父類的run方法時(shí),它默認(rèn)是不帶參數(shù)的,如果需要給它提供參數(shù),需要在類的構(gòu)造函數(shù)中指定,因?yàn)樵诰€程執(zhí)行的過程中,run方法時(shí)線程自己去調(diào)用的,不用我們手動(dòng)調(diào)用,所以沒法直接給傳遞參數(shù),只能在構(gòu)造方法中設(shè)定好參數(shù),然后再run方法中調(diào)用。
針對(duì)join()函數(shù)用法的實(shí)例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# encoding: UTF-8 import threading import time def context(tJoin): print 'in threadContext.' tJoin.start() # 將阻塞tContext直到threadJoin終止。 tJoin.join() # tJoin終止后繼續(xù)執(zhí)行。 print 'out threadContext.' def join(): print 'in threadJoin.' time.sleep( 1 ) print 'out threadJoin.' tJoin = threading.Thread(target = join) tContext = threading.Thread(target = context, args = (tJoin,)) tContext.start() |
執(zhí)行結(jié)果:
1
2
3
4
|
in threadContext. in threadJoin. out threadJoin. out threadContext. |
解析:
主程序中這句tJoin = threading.Thread(target=join)執(zhí)行后,只是創(chuàng)建了一個(gè)線程對(duì)象tJoin,但并未啟動(dòng)該線程。
1
2
|
tContext = threading.Thread(target = context, args = (tJoin,)) tContext.start() |
上面這兩句執(zhí)行后,創(chuàng)建了另一個(gè)線程對(duì)象tContext并啟動(dòng)該線程(打印in threadContext.),同時(shí)將tJoin線程對(duì)象作為參數(shù)傳給context函數(shù),在context函數(shù)中,啟動(dòng)了tJoin這個(gè)線程,同時(shí)該線程又調(diào)用了join()函數(shù)(tJoin.join()),那tContext線程將等待tJoin這線程執(zhí)行完成后,才能繼續(xù)tContext線程后面的,所以先執(zhí)行join()函數(shù),打印輸出下面兩句:
1
2
|
in threadJoin. out threadJoin. |
tJoin線程執(zhí)行結(jié)束后,繼續(xù)執(zhí)行tContext線程,于是打印輸出了out threadContext.,于是就看到我們上面看到的輸出結(jié)果,并且無論執(zhí)行多少次,結(jié)果都是這個(gè)順序。但如果將context()函數(shù)中tJoin.join()這句注釋掉,再執(zhí)行該程序,打印輸出的結(jié)果順序就不定了,因?yàn)榇藭r(shí)這兩線程就是并發(fā)執(zhí)行的。
multiprocessing.dummy
Python中線程multiprocessing模塊與進(jìn)程使用的同一模塊。使用方法也基本相同,唯一不同的是,from multiprocessing import Pool這樣導(dǎo)入的Pool表示的是進(jìn)程池;
from multiprocessing.dummy import Pool這樣導(dǎo)入的Pool表示的是線程池。這樣就可以實(shí)現(xiàn)線程里面的并發(fā)了。
線程池實(shí)例:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import time from multiprocessing.dummy import Pool as ThreadPool #給線程池取一個(gè)別名ThreadPool def run(fn): time.sleep( 2 ) print fn if __name__ = = '__main__' : testFL = [ 1 , 2 , 3 , 4 , 5 ] pool = ThreadPool( 10 ) #創(chuàng)建10個(gè)容量的線程池并發(fā)執(zhí)行 pool. map (run, testFL) pool.close() pool.join() |
執(zhí)行結(jié)果:
這里的pool.map()函數(shù),跟進(jìn)程池的map函數(shù)用法一樣,也跟內(nèi)建的map函數(shù)一樣。
總結(jié)
以上就是本文關(guān)于Python多線程threading和multiprocessing模塊實(shí)例解析的全部內(nèi)容,希望對(duì)大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關(guān)專題,如有不足之處,歡迎留言指出。感謝朋友們對(duì)本站的支持!
原文鏈接:http://blog.csdn.net/seetheworld518/article/details/49639885