python多線程,python多線程編程

來源:互聯網
上載者:User

python多線程,python多線程編程

首先,說明一下多線程的應用情境:當python處理多個任務時,這些任務本質是非同步,需要有多個並發事務,各個事務的運行順序可以是不確定的、隨機的、不可預測的。計算密集型的任務可以順序執行分隔成的多個子任務,也可以用多線程的方式處理。但I/O密集型的任務就不好以單線程方式處理了,如果不用多線程,只能用一個或多個計時器來處理實現。

      下面說一下進程與線程:進程(有時叫重量級進程),是程式的一次執行,正如我們在centos中,ps -aux | grep something 的時候,總有一個他自身產生的進程,也就是這個grep進程,每個進程有自己的地址空間、記憶體、資料棧、及其他記錄其運行軌跡的輔助資料,因此各個進程也不能直接共用資訊,只能用處理序間通訊(IPC)。

      線程(輕量級進程),與進程最大的區別是,所有的線程運行在同一個進程中,共用相同的運行環境,共用同一片資料空間。所以線程之間可以比進程之間更方便的共用資料以及相互連訊,並發執行完成事務。

      為了方便理解記憶進程與線程的關係,我們可以做一個類比:把cpu比作一個搬家公司,而這家搬家公司只有一輛車(進程)來供使用,開始,這家搬家公司很窮,只有一個員工(單線程),那麼,這個搬家公司一天,最多隻能搬5家,後來,老闆賺到錢了,他沒買車,而是多雇了n個員工(多線程),這樣,每個員工會被安排每次只搬一家,然後就去休息,把車讓出來,讓其他人搬下一家,這看起來其實並沒有提高多少效率,反而增加了成本是吧,這是因為GIL(Global Interpreter Lock) 全域解譯器鎖,保證了安全執行緒(保證資料被安全讀取),即同時只能有一個線程在CPU上運行,這是python特有的機制,也就是說,即使你的運行環境具有雙CPU,python虛擬機器也只會使用一個cpu,也就是說GIL 直接導致 CPython 不能利用物理多核的效能加速運算。具體的詳細解釋(曆史遺留問題,硬體發展太快)可以參考這篇部落格:

      http://blog.sina.com.cn/s/blog_64ecfc2f0102uzzf.html

      在python核心編程中,作者極力建議我們不要使用thread模組,而是要使用threading模組,原因如下:

   1、當主線程退出時,所有其他線程沒有被清除就退出了,thread模組無法保護所有子線程的安全退出。即,thread         模組不支援守護進程。

   2、thread模組的屬性有可能會與threading出現衝突。

   3、低層級的thread模組的同步原語很少(實際只有一個,應該是sleep)。

一、thread模組 

 以下是不使用GIL和使用GIL的兩個範例程式碼:

 1.不使用GIL的程式碼範例:

 1 from time import sleep,ctime 2 import thread 3  4 def loop0(): 5     print 'start loop 0 at: ',ctime() 6     sleep(4) 7     print 'loop 0 done at: ',ctime() 8 def loop1(): 9     print 'start loop 1 at: ',ctime()10     sleep(2)11     print 'loop 1 done at: ',ctime()12 def main():13     print 'start at: ',ctime()14     thread.start_new_thread(loop0,())15     thread.start_new_thread(loop1,())16     sleep(6)17     print 'all loop is done, ' ,ctime()18 19 if __name__=='__main__':20     main()21  22 23 輸出結果:24 25 start at:  Thu Jan 28 10:46:27 201626 start loop 0 at:   Thu Jan 28 10:46:27 201627 28 start loop 1 at:   Thu Jan 28 10:46:27 201629 loop 1 done at:  Thu Jan 28 10:46:29 201630 loop 0 done at:  Thu Jan 28 10:46:31 201631 all loop is done,  Thu Jan 28 10:46:33 2016

由以上輸出可以看出,我們成功開啟了兩個線程,並且與主線程同步,在第2s時,loop1先完成,第4s時loop0完成,又過了2s,主線程完成結束。整個主線程經過了6s,loop0和loop1同步完成。

 

2、使用GIL的程式碼範例:

 

 1 import thread 2 from time import sleep,ctime 3 loops = [4,2] 4 def loop(nloop,nsec,lock): 5     print 'start loop',nloop,'at: ',ctime() 6     sleep(nsec) 7     print 'loop',nloop,'done at:',ctime() 8     lock.release() 9 def main():10     print 'starting at:',ctime()11     locks = []12     nloops = range(len(loops))13 14     for i in nloops:15         lock = thread.allocate_lock()                          #建立鎖的列表,存在locks中16         lock.acquire()                         17         locks.append(lock)                                      18     for i in nloops:19         thread.start_new_thread(loop,(i,loops[i],locks[i]))    #建立線程,參數為迴圈號,睡眠時間,鎖20     for i in nloops:21         while locks[i].locked():                              #等待迴圈完成,解鎖22             pass23     print 'all DONE at:',ctime()24 if __name__ == '__main__':25     main()26  27 28 以上輸出如下:29 30 starting at: Thu Jan 28 14:59:22 201631 start loop  0  at:   Thu Jan 28 14:59:22 201632 33 start loop  1  at:   Thu Jan 28 14:59:22 201634 loop 1 done at: Thu Jan 28 14:59:24 201635 loop 0 done at: Thu Jan 28 14:59:26 201636 all DONE at: Thu Jan 28 14:59:26 2016

 

曆時4秒,這樣效率得到提高,也比在主線程中用一個sleep()函數來計時更為合理。

 

二、threading模組

1、Thread類

在thread類中,可以用以下三種方法來建立線程:

(1)建立一個thread執行個體,傳給它一個函數

(2)建立一個thread執行個體,傳給它一個可調用的類對象

(3)從thread派生出一個子類,建立這個子類的對象

方法(1)

 1 __author__ = 'dell' 2 import threading 3 from time import sleep,ctime 4 def loop0(): 5     print 'start loop 0 at:',ctime() 6     sleep(4) 7     print 'loop 0 done at:',ctime() 8 def loop1(): 9     print 'start loop 1 at:',ctime()10     sleep(2)11     print 'loop 1 done at:',ctime()12 def main():13     print 'starting at:',ctime()14     threads = []15     t1 = threading.Thread(target=loop0,args=())          #建立線程16     threads.append(t1)17     t2 = threading.Thread(target=loop1,args=())18     threads.append(t2)19     for t in threads:20         t.setDaemon(True)<span style="white-space:pre">    </span>      #開啟守護線程(一定要在start()前調用)21         t.start()<span style="white-space:pre">        </span>      #開始線程執行22     for t in threads:<span style="white-space:pre">                    </span>23         t.join()<span style="white-space:pre">        </span>      #將程式掛起阻塞,直到線程結束,如果給出數值,則最多阻塞timeout秒24 25 if __name__ == '__main__':26     main()27     print 'All DONE at:',ctime()28 29 在這裡,就不用像thread模組那樣要管理那麼多鎖(分配、擷取、釋放、檢查等)了,同時我也減少了迴圈的代碼,直接自己編號迴圈了,得到輸出如下:30  31 32 starting at: Thu Jan 28 16:38:14 201633 start loop 0 at: Thu Jan 28 16:38:14 201634 start loop 1 at: Thu Jan 28 16:38:14 201635 loop 1 done at: Thu Jan 28 16:38:16 201636 loop 0 done at: Thu Jan 28 16:38:18 201637 All DONE at: Thu Jan 28 16:38:18 2016

結果相同,但是從代碼的邏輯來看,要清晰的多了。其他兩種在此就不貼出代碼了。執行個體化一個Thread與調用thread.start_new_thread直接最大的區別就是新的線程不會立即開始執行,也就是說,在threading模組的Thread類中當我們執行個體化之後,再調用.start()函數後被統一執行,這使得我們的程式具有很好的同步特性。

下面是單線程與多線程的一個對比樣本,分別以乘除完成兩組運算,從而看出多線程對效率的提高

 1 from time import ctime,sleep 2 import threading 3  4 def multi(): 5     num1 = 1 6     print 'start mutiple at:',ctime() 7     for i in range(1,10): 8        num1 = i*num1 9        sleep(0.2)10     print 'mutiple finished at:',ctime()11     return num112 def divide():13     num2 = 10014     print 'start division at:',ctime()15     for i in range(1,10):16         num2 = num2/i17         sleep(0.4)18     print 'division finished at:',ctime()19     return num220 def main():21     print '---->single Thread'22     x1 = multi()23     x2 = divide()24     print 'The sum is ',sum([x1,x2]),'\nfinished singe thread',ctime()25 26     print '----->Multi Thread'27     threads = []28     t1 = threading.Thread(target=multi,args=())29     threads.append(t1)30     t2 = threading.Thread(target=divide,args=())31     threads.append(t2)32     for t in threads:33         t.setDaemon(True)34         t.start()35     for t in threads:36         t.join()37 38 if __name__ == '__main__':39     main()40 41 結果如下:42 43  44 45 ---->single Thread46 47 start mutiple at: Thu Jan 28 21:41:18 201648 49 mutiple finished at: Thu Jan 28 21:41:20 201650 51 start division at: Thu Jan 28 21:41:20 201652 53 division finished at: Thu Jan 28 21:41:24 201654 55 The sum is  362880 56 57 finished singe thread Thu Jan 28 21:41:24 201658 59 ----->Multi Thread60 61 start mutiple at: Thu Jan 28 21:41:24 201662 63 start division at: Thu Jan 28 21:41:24 201664 65 mutiple finished at: Thu Jan 28 21:41:26 201666 67 division finished at: Thu Jan 28 21:41:27 201668 69 The sum is : 362880

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.