python多线程与GIL
目錄
1.GIL? ? ??
1.1 為什么要有GIL
1.2 GIL的運作方式
1.3?GIL帶來的問題
2.多線線程
2.1 線程的調度和啟動
3.線程構造與使用
3.1調用Thread類構造器創建線程
3.2繼承Thread類創建線程類
在學習python多線程過程,學習一些很不錯的文章和教程,這里進行摘抄并總結歸納一下,便于加深自己的理解
1.GIL? ? ??
? ? GIL的全稱是全局解釋器鎖(Global Interpreter Lock),GIL并不是Python的特性,它是在實現Python解析器(CPython)時所引入的一個概念。就好比C++,可以用不同的編譯器來編譯成可執行代碼。有名的編譯器例如GCC,INTEL C++,Visual C++等。Python也一樣,同樣一段代碼可以通過CPython,PyPy,Psyco等不同的Python執行環境來執行。像其中的JPython就沒有GIL。然而因為CPython是大部分環境下默認的Python執行環境。所以在很多人的概念里CPython就是Python,也就想當然的把GIL歸結為Python語言的缺陷。所以這里要先明確一點:GIL并不是Python的特性,Python完全可以不依賴于GIL。
1.1 為什么要有GIL
相比于使用更細粒度的鎖,GIL具有以下優點:
1.2 GIL的運作方式
1.3?GIL帶來的問題
因為有GIL的存在,由CPython做解釋器(虛擬機)的多線程Python程序只能利用多核處理器的一個核來運行。
例如,我們將一個8線程的JAVA程序運行在4核的處理器上,那么每個核會運行1個線程,然后利用時間片輪轉,輪流運行每一個線程。但是,我們將一個8線程的Python程序(由CPython作解釋器)運行在一個4核處理器上,那么總共只會有1個核在工作,8個線程都要在這一個核上面時間片輪轉。
所以說,用Python做多線程的最佳場景是處理I/O密集型應用,例如網絡爬蟲,因為I/O的速度比CPU的速度慢非常多,這樣不同線程的上下文切換(context switch)的時間就可以忽略不計了。如果做CPU密集型任務,則使用多進程可以更好地利用CPU的多核性能。另外,使用gevent協程可以避免多線程的上下文切換的問題
在python中使用都是操作系統級別的線程,linux中使用的pthread,window使用的是其原生線程
2.多線線程
多線程的好處自不必贅述,通過一個簡單的例子來看一下,參考關于播放音樂和視頻
先看看單線程的
#coding=utf-8 import threading from time import ctime,sleepdef music(func):for i in range(2):print("I was listening to %s. %s" %(func,ctime()))sleep(1)def move(func):for i in range(2):print("I was at the %s! %s" %(func,ctime()))sleep(5)if __name__ == '__main__':print("all start %s" %ctime())music(u'Music')move(u'Vedio')print("all over %s" %ctime())輸出如下:
但實際上詩就像MV一樣,這兩個是一般是同時開展的
#coding=utf-8 import threading from time import ctime,sleepdef music(func):for i in range(2):print("I was listening to %s. %s" %(func,ctime()))sleep(1)def move(func):for i in range(2):print("I was at the %s! %s" %(func,ctime()))sleep(5)threads = [] t1 = threading.Thread(target=music,args=(u'Music',)) threads.append(t1) t2 = threading.Thread(target=move,args=(u'Vedio',)) threads.append(t2)if __name__ == '__main__':print("all start %s" % ctime())for t in threads:t.setDaemon(True)t.start()t.join()print("all over %s" %ctime())?
1.join()方法,用于等待線程終止。join()的作用是,在子線程完成運行之前,這個子線程的父線程將一直被阻塞等待子線程結束
2.程序中一般有主線程和子線程,兩種線程都執行完畢,認為是程序執行結束。setDaemon(True)將線程聲明為守護線程,此類線程的特點是,當程序中主線程及所有非守護線程執行結束時,未執行完畢的守護線程也會隨之消亡(進行死亡狀態),程序將結束運行。Python 解釋器的垃圾回收機制就是守護線程的典型代表,當程序中所有主線程及非守護線程執行完畢后,垃圾回收機制也就沒有再繼續執行的必要了。需要注意的一點是,線程對象調用 daemon 屬性必須在調用 start() 方法之前,否則 Python 解釋器將報 RuntimeError 錯誤
參考?Python daemon守護線程詳解
輸入結果如下:
2.1 線程的調度和啟動
python 中一個線程對應c語言中一個線程。GIL使得同一個時刻只有一個線程運行在cpu上執行字節碼,無法將多個線程映射到多個cpu上執行,GIL會根據執行的字節碼行數以及時間釋放GIL,在遇到IO操作時會主動釋放。python提供了_thread 和threading 兩個模塊來支持多線程,其中_thread提供低級別的,原始的線程支持,以及一個簡單的鎖,但是一般不建議使用_thread模塊;而threading模塊則提供了功能豐富的多線程支持。
通過一個對一個全局變量進行增減操作,來解釋上述藍色字體描述
total = 0 def add(name):global totalfor i in range(10000):total += 1print('當前是%s線程,i的值為%s' % (name,total))def desc(name):global totalfor i in range(10000):total -= 1print('當前是%s線程,i的值為%s' % (name,total))import threading thread1 = threading.Thread(target=add,args=('thread1',)) thread2 = threading.Thread(target=desc,args=('thread2',))thread1.start() thread2.start()thread1.join() thread2.join()print(total)輸入結果如下所示:
從輸出結果來看,并不是等待其中一個thread跑完了再去跑另一個線程任務
3.線程構造與使用
python 主要提供兩種方式來創建線程:
- 使用threading模塊的Thread類的構造器創建線程
- 繼承 threading模塊的Thread類創建線程類
3.1調用Thread類構造器創建線程
調用Thread類的構造器創建線程,直接調用threading.Thread類的如下構造器創建線程
__init__(self, group= None, target=None, name= None, args=(), kwargs= None, *, daemon= None)target :指定該線程要調度的目標方法。只傳函數名,不傳函數,即不加() args :指定一個元組,以位置參數的形式為target指定的函數傳入參數。元組的第一個參數傳給target的第一個,以此類推。 kwargs:指定一個字典,以關鍵字參數的形式為target指定的函數傳入參數 daemon: 指定所構建的線程是否為后臺線程。調用Thread類的構造器創建并啟動多線程的步驟:
調用Thread類的構造器創建線程對象。在創建線程對象時。target參數指定的函數將作為線程的執行實體
調用線程對象的start()方法啟動線程。
執行結果如下:
可以看到在循環變量達到20時,創建并且啟動了兩個新線程。所以此時一共有三個線程,但是三個線程之間的執行沒有沒有先后順序,他們的執行方式為Thread-1執行一段時間Thread-2或MainThread獲得CPU執行一段時間。
- threading.current_thread():它是threading模塊的函數,該函數總是返回當前正在執行的線程對象
- getName() 它是Thread類的實例方法,該方法返回調用它的線程名字
-setName() 方法可以為線程設置名字,getName和setName 兩個方法可通過name屬性來代替。
3.2繼承Thread類創建線程類
-
定義Thread類的子類,并重寫該類的run()方法。run()方法的方法體就代表了線程需要完成的任務,因此把run()方法稱為線程執行體。
-
創建Thread子類的實例,即創建線程對象
-
調用線程對象的start()方法來啟動線程
輸出結果如下:
?
參考 python中的GIL詳解
參考?python 多線程
參考?python并發編程
總結
以上是生活随笔為你收集整理的python多线程与GIL的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Django实现对数据库数据增删改查(二
- 下一篇: centos桥接模式网络配置