所有代碼來自python核心編程
參考python核心編程一書,學(xué)習(xí)多線程工作模式,多線程實現(xiàn)主要模塊thread,threading,Queue等。
首先實現(xiàn)單線程一段代碼:
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()
運行結(jié)果如下:能看到兩個函數(shù)按順序在6秒內(nèi)完成
starting at: Thu Jun 08 16:42:00 2017
start loop 0 at: Thu Jun 08 16:42:00 2017
loop 0 done at: Thu Jun 08 16:42:04 2017
start loop 1 at: Thu Jun 08 16:42:04 2017
loop 1 done at: Thu Jun 08 16:42:06 2017
all done at: Thu Jun 08 16:42:06 2017
我們將首先利用thread修改代碼,來看看效果
import thread
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()
a=thread.start_new_thread(loop0,())
b=thread.start_new_thread(loop1,())
sleep(6)
print 'all done at:', ctime()
if __name__ == '__main__':
main()
運行結(jié)果如下:我們能看到loop0和loop1是并發(fā)執(zhí)行的
starting at: Thu Jun 08 16:44:44 2017
start loop 1 at: Thu Jun 08 16:44:44 2017
start loop 0 at: Thu Jun 08 16:44:44 2017
loop 1 done at: Thu Jun 08 16:44:46 2017
loop 0 done at: Thu Jun 08 16:44:48 2017
all done at: Thu Jun 08 16:44:50 2017
我們添加了sleep(6)來實現(xiàn)子線程與主線程的同步,我們可以嘗試把sleep(6)改為sleep(2)來看看效果:
starting at: Thu Jun 08 16:47:52 2017
start loop 0 at: Thu Jun 08 16:47:52 2017
start loop 1 at: Thu Jun 08 16:47:52 2017
all done at: Thu Jun 08 16:47:54 2017
Unhandled exception in thread started by
sys.excepthook is missing
lost sys.stderr
直接導(dǎo)致子線程未結(jié)束時候主線程已經(jīng)退出
下面的例子我們再利用thread的鎖機制來保證線程的同步
import thread
from time import sleep,ctime
loops = [4,2]
def loop(nloop,nsec,lock):
print 'start loop',nloop, 'at:', ctime()
sleep(nsec)
print 'loop', nloop, 'done at:', ctime()
lock.release()
def main():
print 'starting at:', ctime()
locks = []
nloops = range(len(loops))
for i in nloops:
lock = thread.allocate_lock()
lock.acquire()
locks.append(lock)
for i in nloops:
thread.start_new_thread(loop,(i,loops[i],locks[i]))
#sleep(1)
for i in nloops:
while locks[i].locked():
pass
print 'all done at:', ctime()
if __name__ == '__main__':
main()
運行效果:
starting at: Thu Jun 08 17:05:59 2017
start loop 0 at: Thu Jun 08 17:05:59 2017start loop
1 at: Thu Jun 08 17:05:59 2017
loop 1 done at: Thu Jun 08 17:06:01 2017
loop 0 done at: Thu Jun 08 17:06:03 2017
all done at: Thu Jun 08 17:06:03 2017
我們能看到兩個線程是并發(fā)的,并且主線程等子線程結(jié)束后才結(jié)束的。但是與書上不一致的是啟動線程中的輸出是亂掉了,不清楚為何和書上為何不一致。
根據(jù)作者的解釋建議我們盡量不要使用thread模塊,而使用更高級的threading模塊。
下面我們先看下threading模塊的對象:

利用threading模塊的thread類有三種方法:
? 創(chuàng)建Thread 的實例,傳給它一個函數(shù)。
? 創(chuàng)建Thread 的實例,傳給它一個可調(diào)用的類實例。
? 派生Thread 的子類,并創(chuàng)建子類的實例。
首先利用第一種方法來修改上面的例子:
? 創(chuàng)建Thread 的實例,傳給它一個函數(shù)。
import threading
from time import sleep, ctime
loops = [4, 2]
def loop(nloop, nsec):
print 'start loop', nloop, 'at:', ctime()
sleep(nsec)
print 'loop', nloop, '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()
運行效果:跟之前的代碼對比,我們用一組thread對象代替了之前實現(xiàn)的鎖,并且代碼不會立即執(zhí)行,只在你希望它執(zhí)行的時候執(zhí)行;另外使用join()方法比等待鎖釋放的無限循環(huán)更清晰。
starting at: Fri Jun 09 10:21:45 2017
start loop 0 at: Fri Jun 09 10:21:45 2017
start loop 1 at: Fri Jun 09 10:21:45 2017
loop 1 at: Fri Jun 09 10:21:47 2017
loop 0 at: Fri Jun 09 10:21:49 2017
all done at: Fri Jun 09 10:21:49 2017
接著我們使用第二種方法來修改代碼
? 創(chuàng)建Thread 的實例,傳給它一個可調(diào)用的類實例。
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.func(*self.args)
def loop(nloop, nsec):
print 'start loop', nloop, 'at:', ctime()
sleep(nsec)
print 'loop', nloop, '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()
運行結(jié)果:在上個代碼中添加一個新類ThreadFunc即得到這個代碼,比起一個函數(shù)有更好的靈活性,而不僅僅是單個函數(shù)
starting at: Fri Jun 09 10:31:34 2017
start loop 0 at: Fri Jun 09 10:31:34 2017
start loop 1 at: Fri Jun 09 10:31:34 2017
loop 1 at: Fri Jun 09 10:31:36 2017
loop 0 at: Fri Jun 09 10:31:38 2017
all done at: Fri Jun 09 10:31:38 2017
我們再看看第三種方式的代碼:
? 派生Thread 的子類,并創(chuàng)建子類的實例。
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):
self.func(*self.args)
def loop(nloop, nsec):
print 'start loop', nloop, 'at:', ctime()
sleep(nsec)
print 'loop', nloop, '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()
本例直接對Thread子類化,使我們具有更多的靈活性。
使用這種模式必須首先先調(diào)用基類的構(gòu)造函數(shù),即

還有之前的call()特殊方法必須寫為run()。
下面是運行結(jié)果:
starting at: Fri Jun 09 12:10:54 2017
start loop 0 at: Fri Jun 09 12:10:54 2017
start loop 1 at: Fri Jun 09 12:10:54 2017
loop 1 at: Fri Jun 09 12:10:56 2017
loop 0 at: Fri Jun 09 12:10:58 2017
all done at: Fri Jun 09 12:10:58 2017
如果我們要獲取子線程返回的結(jié)果怎么處理呢,下篇文章我們在討論。