python之多線程


聲明:示例來源《python核心編程》

前言

  單線程處理多個外部輸入源的任務只能使用I/O多路復用,如:select,poll,epoll。

  特別值得注意的是:由於一個串行程序需要從每個 I/O 終端通道來檢查用戶的輸入,程序在讀取 I/O 終端通道時不能阻塞,因為用戶輸入的到達時間是不確定的,並且阻塞會妨礙其他 I/O 通道的處理。

  select,poll,epoll本質上都是同步I/O,因為他們都需要在讀寫事件就緒后自己負責進行讀寫,也就是說這個讀寫過程是阻塞的,而異步I/O則無需自己負責進行讀寫,異步I/O的實現只負責把數據從內核拷貝到用戶空間。

為此就引出了我們的主體多線程,多線程的特點:

  1. 本質上是異步的
  2. 需要多個並發活動
  3. 每個活動的處理順序可能是不確定的,或者說是隨機的、不可預測的。

什么是進程?
進程就是一個執行中的程序。每個進程都擁有自己的地址空間、內存、數據棧以及其他用於跟蹤執行的輔助數據。操作系統管理其上所有進程的執行,並為這些進程合理地分配時間。進程也可以通過派生( fork 或 spawn)新的進程來執行其他任務,不過因為每個新進程也都擁有自己的內存和數據棧等,所以只能采用進程間通信( IPC)的方式共享信息。

什么是線程?
線程(有時候稱為輕量級進程)與進程類似,不過它們是在同一個進程下執行的,並共享相同的上下文。可以將它們認為是在一個主進程或“主線程”中並行運行的一些“迷你進程”。

python中的多線程實現

threading模塊中的對象列表

ps:我們通過python實現多線程編程,主要用到的是threading.Thread對象

Thread對象常用屬性和方法

多線程示例

context: python2.7.13

python通過Thread對象創建一個多線程實例,主要有3種方式:

  1. 創建 Thread 的實例,傳給它一個函數。
  2. 創建 Thread 的實例,傳給它一個可調用的類實例。
  3. 派生 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
resault

示例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
resault

示例3:派生 Thread 的子類,並創建子類的實例。

自定義類MyThread

  1. 文件名:mythread.py,
  2. 內容: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
resault

  以單線程模式運行時,只是簡單地依次調用每個函數,並在函數執行結束后立即顯示相應的結果。

  而以多線程模式運行時,並不會立即顯示結果。 因為我們希望讓 MyThread 類越通用越好(有輸出和沒有輸出的調用都能夠執行),我們要一直等到所有線程都執行結束,然后調用get_res()方法來最終顯示每個函數的返回值。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM