聲明:示例來源《python核心編程》
前言
單線程處理多個外部輸入源的任務只能使用I/O多路復用,如:select,poll,epoll。
特別值得注意的是:由於一個串行程序需要從每個 I/O 終端通道來檢查用戶的輸入,程序在讀取 I/O 終端通道時不能阻塞,因為用戶輸入的到達時間是不確定的,並且阻塞會妨礙其他 I/O 通道的處理。
select,poll,epoll本質上都是同步I/O,因為他們都需要在讀寫事件就緒后自己負責進行讀寫,也就是說這個讀寫過程是阻塞的,而異步I/O則無需自己負責進行讀寫,異步I/O的實現只負責把數據從內核拷貝到用戶空間。
為此就引出了我們的主體多線程,多線程的特點:
- 本質上是異步的
- 需要多個並發活動
- 每個活動的處理順序可能是不確定的,或者說是隨機的、不可預測的。
什么是進程?
進程就是一個執行中的程序。每個進程都擁有自己的地址空間、內存、數據棧以及其他用於跟蹤執行的輔助數據。操作系統管理其上所有進程的執行,並為這些進程合理地分配時間。進程也可以通過派生( fork 或 spawn)新的進程來執行其他任務,不過因為每個新進程也都擁有自己的內存和數據棧等,所以只能采用進程間通信( IPC)的方式共享信息。
什么是線程?
線程(有時候稱為輕量級進程)與進程類似,不過它們是在同一個進程下執行的,並共享相同的上下文。可以將它們認為是在一個主進程或“主線程”中並行運行的一些“迷你進程”。
python中的多線程實現
threading模塊中的對象列表
ps:我們通過python實現多線程編程,主要用到的是threading.Thread對象
Thread對象常用屬性和方法
多線程示例
context: python2.7.13
python通過Thread對象創建一個多線程實例,主要有3種方式:
- 創建 Thread 的實例,傳給它一個函數。
- 創建 Thread 的實例,傳給它一個可調用的類實例。
- 派生 Thread 的子類,並創建子類的實例。
ps:我們通常會選擇第一個或第三個方案。當你需要一個更加符合面向對象的接口時,會選擇后者。所以,建議使用第三種方案,它是最適合你的應用和未來擴展的方法。
示例1:創建 Thread 的實例,傳給它一個函數。
1 #!/usr/bin/env python 2 #-*- coding:utf-8 -*- 3 import threading 4 from time import sleep,ctime 5 6 loops = [4,2] 7 8 def loop(nloop,nsec): 9 print "start loop",nloop,"at:",ctime() 10 sleep(nsec) 11 print "loop",nloop,'done at:',ctime() 12 13 def main(): 14 print "Starting at:",ctime() 15 threads = [] 16 nloops = range(len(loops)) 17 #完成所有線程分配,並不立即開始執行 18 for i in nloops: 19 t = threading.Thread(target=loop,args=(i,loops[i])) 20 threads.append(t) 21 #開始調用start方法,同時開始所有線程 22 for i in nloops: 23 threads[i].start() 24 #join方法:主線程等待所有子線程執行完成,再執行主線程接下來的操作。 25 for i in nloops: 26 threads[i].join() 27 28 print "All done at:",ctime() 29 if __name__=="__main__": 30 main()

Starting at: Sun Jun 18 10:00:49 2017 start loop 0 at: Sun Jun 18 10:00:49 2017 start loop 1 at: Sun Jun 18 10:00:49 2017 loop 1 done at: Sun Jun 18 10:00:51 2017 loop 0 done at: Sun Jun 18 10:00:53 2017 All done at: Sun Jun 18 10:00:53 2017 Process finished with exit code 0
示例2:創建 Thread 的實例,傳給它一個可調用的類實例

#!/usr/bin/env python #-*- encoding:utf-8 -*- 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 #使類具有函數行為,就像函數的代理(proxy) def __call__(self): 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) #開始調用start方法,同時開始所有線程 for i in nloops: threads[i].start() #join方法等待子線程執行完成,再執行主線程接下來的操作。 for i in nloops: threads[i].join() print "All done at:",ctime() if __name__=="__main__": main()

Starting at: Sun Jun 18 10:03:52 2017 start loop 0 at: Sun Jun 18 10:03:52 2017 start loop 1 at: Sun Jun 18 10:03:52 2017 loop 1 done at: Sun Jun 18 10:03:54 2017 loop 0 done at: Sun Jun 18 10:03:56 2017 All done at: Sun Jun 18 10:03:56 2017 Process finished with exit code 0
示例3:派生 Thread 的子類,並創建子類的實例。
自定義類MyThread
- 文件名:mythread.py,
- 內容:MyThread為threading.Thread的派生類
#!/usr/bin/env python #-*- coding:utf-8 -*- import threading from time import sleep,ctime class MyThread(threading.Thread): def __init__(self,func,args,name=""): threading.Thread.__init__(self) self.name = name self.func = func self.args = args def get_res(self): return self.res def run(self): print "Starting",self.name,"at:",ctime() self.res = self.func(*self.args) print self.name,"finish at:",ctime()
菲波那切數列,階乘,累加單線程也多線程對比
#!/usr/bin/env python #-*- coding:utf-8 -*- #MyThread為自定義的threading.Thread的派生類 from mythread import MyThread from time import sleep,ctime #斐波那契數列 def fib(x): sleep(0.005) if x < 2:return 1 return (fib(x-2)+fib(x-1)) #階乘 def fac(x): sleep(0.1) if x < 2:return 1 return (x*fac(x-1)) #累加 def sum(x): sleep(0.1) if x < 2:return 1 return (x + sum(x-1)) funcs = [fib,fac,sum] n = 12 def main(): nfuncs = range(len(funcs)) print "---SINGLE THREAD---" for i in nfuncs: print "Starting",funcs[i].__name__,"at:",ctime() print funcs[i](n) print funcs[i].__name__,"finish at:",ctime() print "\n---MULTIPLE THREADS---" threads = [] for i in nfuncs: t = MyThread(funcs[i],(n,),funcs[i].__name__) threads.append(t) for i in nfuncs: threads[i].start() for i in nfuncs: threads[i].join() print threads[i].get_res() print "All Done!" if __name__ == '__main__': main()

---SINGLE THREAD--- Starting fib at: Sun Jun 18 09:12:17 2017 233 fib finish at: Sun Jun 18 09:12:24 2017 Starting fac at: Sun Jun 18 09:12:24 2017 479001600 fac finish at: Sun Jun 18 09:12:26 2017 Starting sum at: Sun Jun 18 09:12:26 2017 78 sum finish at: Sun Jun 18 09:12:27 2017 ---MULTIPLE THREADS--- Starting fib at: Sun Jun 18 09:12:27 2017 Starting fac at: Sun Jun 18 09:12:27 2017 Starting sum at: Sun Jun 18 09:12:27 2017 fac finish at: Sun Jun 18 09:12:28 2017 sum finish at: Sun Jun 18 09:12:28 2017 fib finish at: Sun Jun 18 09:12:34 2017 233 479001600 78 All Done! Process finished with exit code 0
以單線程模式運行時,只是簡單地依次調用每個函數,並在函數執行結束后立即顯示相應的結果。
而以多線程模式運行時,並不會立即顯示結果。 因為我們希望讓 MyThread 類越通用越好(有輸出和沒有輸出的調用都能夠執行),我們要一直等到所有線程都執行結束,然后調用get_res()方法來最終顯示每個函數的返回值。