Python代碼代碼的執(zhí)行由python虛擬機(jī)(也叫解釋器主循環(huán))來控制。Python在設(shè)計(jì)之初就考慮到要在主循環(huán)中,同時(shí)只有一個(gè)線程在執(zhí)行,就像單CPU的系統(tǒng)中運(yùn)行多個(gè)進(jìn)程那樣,內(nèi)存中可以存放多個(gè)程序,但任意時(shí)候,只有一個(gè)程序在CPU中運(yùn)行。同樣,雖然python解釋器可以“運(yùn)行”多個(gè)線程,但在任意時(shí)刻,只有一個(gè)線程在解釋器中運(yùn)行。
對(duì)python虛擬機(jī)的訪問由全局解釋器鎖(GIL)來控制,這個(gè)GIL能保證同一時(shí)刻只有一個(gè)線程在運(yùn)行。在多線程環(huán)境中,python虛擬機(jī)按以下方式執(zhí)行: 1 設(shè)置GIL 2 切換到一個(gè)線程去運(yùn)行 3 運(yùn)行:(a.指定數(shù)量的字節(jié)碼指令,或者b.線程主動(dòng)讓出控制(可以調(diào)用time.sleep())) 4 把線程設(shè)置為睡眠狀態(tài) 5 解鎖GIL 6 重復(fù)以上所有步驟 那么為什么要提出多線程呢?我們首先看一個(gè)單線程的例子。
from time import sleep,ctime
def loop0(): print 'start loop 0 at:',ctime() sleep(4) print 'loop 0 done at:',ctime()
def loop1(): print 'start loop 1 at:',ctime() sleep(2) print 'loop 1 done at:',ctime()
def main(): print 'starting at:',ctime() loop0() loop1() print 'all DONE at:',ctime()
if __name__=='__main__': main()
運(yùn)行結(jié)果: >>> starting at: Mon Aug 31 10:27:23 2009 start loop 0 at: Mon Aug 31 10:27:23 2009 loop 0 done at: Mon Aug 31 10:27:27 2009 start loop 1 at: Mon Aug 31 10:27:27 2009 loop 1 done at: Mon Aug 31 10:27:29 2009 all DONE at: Mon Aug 31 10:27:29 2009 >>> 可以看到單線程中的兩個(gè)循環(huán), 只有一個(gè)循環(huán)結(jié)束后另一個(gè)才開始。 總共用了6秒多的時(shí)間。假設(shè)兩個(gè)loop中執(zhí)行的不是sleep,而是一個(gè)別的運(yùn)算的話,如果我們能讓這些運(yùn)算并行執(zhí)行的話,是不是可以減少總的運(yùn)行時(shí)間呢,這就是我們提出多線程的前提。
1 thread ,這個(gè)模塊一般不建議使用。下面我們直接把以上的例子改一下,演示一下。 from time import sleep,ctime import thread
def loop0(): print 'start loop 0 at:',ctime() sleep(4) print 'loop 0 done at:',ctime()
def loop1(): print 'start loop 1 at:',ctime() sleep(2) print 'loop 1 done at:',ctime() def main(): print 'starting at:',ctime() thread.start_new_thread(loop0,()) thread.start_new_thread(loop1,()) sleep(6) print 'all DONE at:',ctime()
if __name__=='__main__': main() 運(yùn)行結(jié)果: >>> starting at: Mon Aug 31 11:04:39 2009 start loop 0 at: Mon Aug 31 11:04:39 2009 start loop 1 at: Mon Aug 31 11:04:39 2009 loop 1 done at: Mon Aug 31 11:04:41 2009 loop 0 done at: Mon Aug 31 11:04:43 2009 all DONE at: Mon Aug 31 11:04:45 2009 >>> 可以看到實(shí)際是運(yùn)行了4秒兩個(gè)loop就完成了。效率確實(shí)提高了。
2 threading模塊 首先看一下threading模塊中的對(duì)象: Thread :表示一個(gè)線程的執(zhí)行的對(duì)象 Lock :鎖原語對(duì)象 RLock :可重入鎖對(duì)象。使單線程可以再次獲得已經(jīng)獲得的鎖 Condition :條件變量對(duì)象能讓一個(gè)線程停下來,等待其他線程滿足了某個(gè)“條件”,如狀態(tài)的改變或值的改變 Event :通用的條件變量。多個(gè)線程可以等待某個(gè)事件發(fā)生,在事件發(fā)生后,所有的線程都被激活 Semaphore :為等待鎖的線程提供一個(gè)類似“等候室”的結(jié)構(gòu) BoundedSemaphore :與semaphore類似,只是它不允許超過初始值 Timer : 與Thread類似,只是,它要等待一段時(shí)間后才開始運(yùn)行 其中Thread類是你主要的運(yùn)行對(duì)象,它有很多函數(shù),用它你可以用多種方法來創(chuàng)建線程,常用的為以下三種。 創(chuàng)建一個(gè)Thread的實(shí)例,傳給它一個(gè)函數(shù) 創(chuàng)建一個(gè)Thread實(shí)例,傳給它一個(gè)可調(diào)用的類對(duì)象 從Thread派生出一個(gè)子類,創(chuàng)建一個(gè)這個(gè)子類的實(shí)例
Thread類的函數(shù)有: getName(self) 返回線程的名字 | isAlive(self) 布爾標(biāo)志,表示這個(gè)線程是否還在運(yùn)行中 | isDaemon(self) 返回線程的daemon標(biāo)志 | join(self, timeout=None) 程序掛起,直到線程結(jié)束,如果給出timeout,則最多阻塞timeout秒 | run(self) 定義線程的功能函數(shù) | setDaemon(self, daemonic) 把線程的daemon標(biāo)志設(shè)為daemonic | setName(self, name) 設(shè)置線程的名字 | start(self) 開始線程執(zhí)行
下面看一個(gè)例子:(方法一:創(chuàng)建Thread實(shí)例,傳遞一個(gè)函數(shù)給它) from time import sleep,ctime loops=[4,2] def loop(nloop,nsec): print 'start loop',nloop,'at:',ctime() sleep(nsec) print 'loop',nloop,'done at:',ctime() def main(): print 'starting at:',ctime() threads=[] nloops=range(len(loops)) for i in nloops: t=threading.Thread(target=loop,args=(i,loops[i])) threads.append(t) for i in nloops: threads[i].start() for i in nloops: threads[i].join() print 'all done at:',ctime() if __name__=='__main__': main() 可以看到第一個(gè)for循環(huán),我們創(chuàng)建了兩個(gè)線程,這里用到的是給Thread類傳遞了函數(shù),把兩個(gè)線程保存到threads列表中,第二個(gè)for循環(huán)是讓兩個(gè)線程開始執(zhí)行。然后再讓每個(gè)線程分別調(diào)用join函數(shù),使程序掛起,直至兩個(gè)線程結(jié)束。
另外的例子:(方法二:創(chuàng)建一個(gè)實(shí)例,傳遞一個(gè)可調(diào)用的類的對(duì)象) import threading from time import sleep,ctime loops=[4,2] class ThreadFunc(object): def __init__(self,func,args,name=''): self.name=name self.func=func self.args=args def __call__(self): self.res=self.func(*self.args) def loop(nloop,nsec): print 'start loop',nloop,'at:',ctime() sleep(nsec) print 'loop',nloop,'done at:',ctime() def main(): print 'starting at:',ctime() threads=[] nloops=range(len(loops)) for i in nloops: t=threading.Thread(target=ThreadFunc(loop,(i,loops[i]),loop.__name__)) threads.append(t) for i in nloops: threads[i].start() for i in nloops: threads[i].join() print 'all done at:',ctime() if __name__=='__main__': main()
最后的方法:(方法三:創(chuàng)建一個(gè)這個(gè)子類的實(shí)例) import threading from time import sleep,ctime loops=(4,2) class MyThread(threading.Thread): def __init__(self,func,args,name=''): threading.Thread.__init__(self) self.name=name self.func=func self.args=args def run(self): apply(self.func,self.args) def loop(nloop,nsec): print 'start loop',nloop,'at:',ctime() sleep(nsec) print 'loop',nloop,'done at:',ctime()
def main(): print 'starting at:',ctime() threads=[] nloops=range(len(loops)) for i in nloops: t=MyThread(loop,(i,loops[i]),loop.__name__) threads.append(t) for i in nloops: threads[i].start() for i in nloops: threads[i].join() print 'all done at:',ctime() if __name__=='__main__': main() 另外我們可以把MyThread單獨(dú)編成一個(gè)腳本模塊,然后我們可以在別的程序里導(dǎo)入這個(gè)模塊直接使用。 |
|