04.08 python掃盲之九-多線程

python掃盲之九-多線程

書接上回,上一篇咱們講到python的多進程,利用多進程的變成方式,咱們實現了讓計算機在同一時間幹更多事情的功能。今天咱們就來說說,讓計算機幹更多事的另一種方法。多線程

<code>與進程的關係?/<code>

既然說到了線程,咱們首先先了解一下線程與進程的關係。看下面的草圖。

python掃盲之九-多線程


咳咳 ,雖然圖片確實草了一丟丟,但是原理還是很清晰的。根據上圖可以看出,進程包含線程。也就是說默認情況下 一個進程肯定會有一個線程的。多線程也就是在一個進程裡面開出多個線程。多進程裡面也可以包含多線程。

從上圖也可以看出另一個問題,上一節中我們說到,多進程之間是不可以直接通訊的。但是由於多線程是被同一個進程包裹,故多線程中資源共享即可以直接通訊。

<code>GIL全局解釋器鎖?/<code>

網上,或者各種討論會上,python程序員面試中 被經常問到關於python多線程 GIL的問題。GIL的設計理念是無論你啟多少個線程,在同一時間, Python在執行的時候只允許一個線程運行。這個設計理念在其他語言中是沒有的。這種設計是為了保證同一時間內多個線程不會操作或誤操作其他線程正在使用的資源。為了避免尷尬,就好比我在吃米飯,你過來把我的米飯搶走了。。。 同時這種設計也確實影響了python多線程的性能。這個咱們後面細說。

<code>thread和threading/<code>

上面囉七八嗦了很多,下面說一下python實現多線程的方法。Python的標準庫提供了兩個模塊:thread和threading,其中thread是低級模塊,threading是高級模塊。平常咱們使用就使用threading就好。thread在python中改成了_thread。官方也推薦使用threading了。看個例子

#!/usr/bin/env python#-*- coding:utf-8 -*-import threadingdef fun(n):print(n)x1 = threading.Thread(target=fun,args=('x1',))#生成線程x2 = threading.Thread(target=fun,args=('x2',))x1.start()#啟動n1線程x2.start()#啟動n2線程print x1.getName()print x2.getName()#運行x1x2Thread-1Thread-2#上面這段代碼中 我們創建了兩個線程,x1和x2 。語法比較簡單。實際執行你會發現每次運行 輸出的順序都不一樣。

可能有同學注意到上面代碼中,我們建立兩個線程 需要start兩次,難道我們創建100的線程也要這樣麼?那豈不代碼又臭又長了。

#!/usr/bin/env python#-*- coding:utf-8 -*-import threadingdef fun(n):print(n)threads =[]for i in['x1','x2']: threads.append(threading.Thread(target=fun,args=(i,)))if __name__ =='__main__':for t in threads: t.setDaemon(True) t.start()print t.getName()#運行x1Thread-1x2Thread-2#我們對上面的代碼進行了簡單改造,用循環的方式創建多線程。

<code>setDeamon=True?/<code>

你可能發現了上面的代碼中有一段t.setDaemon(True),這個是幹嘛的?

#還是上面的例子咱們稍加改造一下#第一種#!/usr/bin/env python#-*- coding:utf-8 -*-import threadingimport timedef fun(n): time.sleep(1)print(n)threads =[]for i in['x1','x2']: threads.append(threading.Thread(target=fun,args=(i,)))if __name__ =='__main__':for t in threads: t.setDaemon(True)###看這裡哦 t.start()print t.getName()print'走你'#運行Thread-1Thread-2走你#第二種#!/usr/bin/env python#-*- coding:utf-8 -*-import threadingimport timedef fun(n): time.sleep(1)print(n)threads =[]for i in['x1','x2']: threads.append(threading.Thread(target=fun,args=(i,)))if __name__ =='__main__':for t in threads: t.setDaemon(False)##看這裡哦 t.start()print t.getName()print'走你'#運行Thread-1Thread-2走你x2x1##看見兩種代碼運行之後的輸出了麼?

由上面這個例子,得知。serDeamon(False)主線程執行過程中,子線程也在進行,主線程執行完畢後,等待子線程也執行完成後,主線程停止。而serDeamon(True)主線程執行過程中,子線程也在進行,主線程執行完畢後,子線程不論成功與否,主線程均停止。這裡需要特別注意一下!

<code>join()方法/<code>

#!/usr/bin/env python#-*- coding:utf-8 -*-import threadingimport timedef fun(n): time.sleep(1)print(n)threads =[]for i in['x1','x2']: threads.append(threading.Thread(target=fun,args=(i,)))if __name__ =='__main__':for t in threads: t.setDaemon(True) t.start()print t.getName()print'走你' t.join()#看這裡#運行Thread-1Thread-2走你x1x2#還是上面的例子 只是我們在底部加了一個join方法。看到運行結果了沒

join()阻塞當前上下文環境的線程,直到調用此方法的線程終止或到達指定的timeout,主線程才會停止運行,即使設置了setDeamon(True)主線程依然要等待子線程結束。這就達到了serDeamon(False)的效果。

<code>Lock、Rlock/<code>

從英文單詞來看,咱們也知道這只是鎖。沒錯,線程鎖。當某線程可能在執行n條後,CPU接著執行其他線程。為了多個線程同時操作一個內存中的資源時不產生混亂,我們使用鎖。這個鎖是咱們手動設定的。兩者有什麼不同?

  • Lock(指令鎖)是可用的最低級的同步指令。Lock處於鎖定狀態時,不被特定的線程擁有。Lock包含兩種狀態——鎖定和非鎖定,以及兩個基本的方法。可以認為Lock有一個鎖定池,當線程請求鎖定時,將線程至於池中,直到獲得鎖定後出池。池中的線程處於狀態圖中的同步阻塞狀態。

  • RLock(可重入鎖)是一個可以被同一個線程請求多次的同步指令。RLock使用了“擁有的線程”和“遞歸等級”的概念,處於鎖定狀態時,RLock被某個線程擁有。擁有RLock的線程可以再次調用acquire(),釋放鎖時需要調用release()相同次數。

注意,如果你使用的Lock 並且多次獲取鎖時,會導致程序死鎖。但是Rlock就不會。 這是面試問題,要記住啊

實際變成編程中,我們還是推薦使用Rlock。

咱們看個例子來演示一下鎖的使用

#咱們用多線程來輸出1-10的數字#咱們先看一下不加鎖的情況#!/usr/bin/env python#-*- coding:utf-8 -*-import threadingimport timegl_num =0def show(arg):global gl_num time.sleep(1) gl_num +=1print gl_numfor i in range(10): t = threading.Thread(target=show, args=(i,)) t.start()#運行1342567899##亂七八糟#加鎖#!/usr/bin/env python#-*- coding:utf-8 -*-import threadingimport timelock = threading.RLock()gl_num =0def show(arg): lock.acquire()global gl_num time.sleep(1) gl_num +=1print gl_num lock.release()for i in range(10): t = threading.Thread(target=show, args=(i,)) t.start()#運行12345678910#看到區別了麼

好啦,關於多線程就講這麼多,夠用了。關於多進程+多線程的套用方法,童鞋們自己研究一下非常簡單,但是也不推薦使用,這種情況會導致代碼邏輯過於複雜,不利於維護。

說回GIL,業界關於pythonGIL的討論一直是喋喋不休。多線程的併發在Python中就是一個美麗的夢,這句話是沒錯的。確實在某些程度上影響了多線程的性能。那麼我們如何選擇,記得某位大神說過,IO密集型用多線程,計算密集型用多進程。自己體會一下含義吧。我就不做過多解釋了。

歡迎加入python自學交流群:460146436


分享到:


相關文章: