
“唉,還沒畢業(yè)就受到甲方的支配,等以后進了公司可咋整啊?!毙“鬃炖镞@么吐槽,但心理上還是不敢怠慢,只能戀戀不舍地關(guān)掉眼前的游戲,打開了 Python 代碼思考了起來。
“現(xiàn)在的程序是單線程的,那就用多線程模型來優(yōu)化吧,嘿嘿,我太機智了!”小白打心底感謝前段時間里學(xué)習(xí)到的線程的知識?!癙ython 里好像是 threading 模塊負(fù)責(zé)多線程的,就決定是你了,threading !”
花了兩三個小時,小白終于把程序改好了,他長舒一口氣,點擊 Run ,開始測試運行時間。
“woc!怎么運行時間還變長了?”看著屏幕上顯示的測試結(jié)果,小白傻眼了,這多線程怎么不頂用?自己明明是按官方文檔來的??!
debug 無果,小白只好尋求好朋友小明的幫助。
“哈哈哈,你居然用 python 的多線程?你不知道 python 的多線程被很多人稱作「雞肋」嗎?”
“啊,不會吧?還有這種說法?我要是知道我肯定就不用多線程來改了。。??蔀槭裁?python 的多線程就雞肋了?那多線程爬蟲是怎么回事?”
“我先給你解釋下 python 下的多線程是怎么一回事吧。Python 是一門解釋型語言,它的執(zhí)行是由解釋器來控制的,我們一般都會使用默認(rèn)的 Cpython 解釋器,這些我想你應(yīng)該清楚?!?/p>
“那當(dāng)然,這在一開始學(xué) Python 的時候老師就講過了?!?/p>
“那你知不知道什么是 GIL?”小明問道。
小白撓撓頭,尷尬的回答:“我沒聽說過?!?/p>
“也是,你要是知道 GIL ,就能弄明白 Python 的多線程了。GIL,全稱是 Global Interpreter Lock ,全局解釋鎖 ,專門給解釋器用的?!?/p>
“蛤?解釋器還要需要鎖? ”
“這個鎖別有妙用,讓我先考考你,C 語言能不能在用戶態(tài)下做到線程級別的時間片輪轉(zhuǎn)?”
“不能!操作系統(tǒng)里講過了,我還記得上次你教我的內(nèi)容~”小白得意的回答。
關(guān)于線程,詳細(xì)的在這里:
“但是 python 能做到!python 里,解釋器可以記錄每一個線程執(zhí)行了多長時間——時間一到,就能夠切換到另一條線程?!?/p>
“有點意思,聽起來像是解釋器充當(dāng)了操作系統(tǒng)的角色,然后為 python 線程提供了時間片輪轉(zhuǎn)的能力?!?/p>
tobe 注:理論上 C 語言也可以做到,畢竟 python 解釋器就是用 C 語言寫的。
“解釋的很到位,我再說回 GIL 吧,在多核還沒有出現(xiàn)的時候,就已經(jīng)有線程的存在了,GIL 就是拿來給線程加鎖的,當(dāng)一個線程將要執(zhí)行時,解釋器會把 GIL 鎖給這個線程,其他線程因為沒有鎖,是無法運行的。等到持有鎖線程阻塞或者運行 100 個字節(jié)碼,解釋器就會把鎖交給其他線程。”

“但是這個 GIL 鎖是全局(Global)的,也就導(dǎo)致即使是多核情況下,一次也只有一個線程能運行,從整體上看,整個程序是串行的?!?/p>
小白恍然大悟:“怪不得我的程序還變慢了,原來 python 的多線程不僅不能利用多核,還因為線程切換拖慢了我程序的執(zhí)行速度!我想很多人應(yīng)該都遇到過我這個問題吧,Python 社區(qū)為什么不修改這一特性,讓多線程也做到并行呢?”
小明嘆了口氣:“哪有這么簡單,修改鎖的設(shè)計是很難的,我聽說 MYSQL 拆分 buffer pool mutex 這個全局鎖花了好多年呢。不過 Python 社區(qū)為此還是做了不少挽救工作的,比如在線程睡覺(sleep),等待連接的時候主動釋放 GIL,讓其他線程繼續(xù)執(zhí)行。拿爬蟲程序來說吧,單個爬蟲總會花時間在下載網(wǎng)頁上,很多 CPU 時間就浪費掉了, 提供 sleep 機制后,這些爬蟲可以在等待下載時釋放 GIL 鎖,把機會讓給其他爬蟲,這樣整體運行速度能夠得到大幅提升?!?/p>
“我好像明白了,”小白感覺自己被打通了任督二脈:“也就是說 Python 的多線程適合 I/O 密集型的程序,但是對計算密集型程序就不那么友好了~ 誒等等,那我怎么辦?我還打算用多線程優(yōu)化我的程序呢!”
“讓 python 利用多核的方法還是有的,比如說,讓 python 調(diào)用 C 語言的代碼,在 C 語言里實現(xiàn)多線程,因為 C 語言里沒有 GIL 鎖,這些線程不會受到 GIL 的約束,也就能并行了。”

小白一個勁兒搖頭:“不不不不不,好不容易寫完的 Python 代碼,你讓我改成 C?這不是要我的命嗎!我拒絕!”
“這不是還有第二種方法嘛——使用多進程,Python 里有個 multiprocessing 模塊,可以創(chuàng)建多個進程,因為不同的進程使用不同的解釋器,所以它們有各自的 GIL,互不干擾,自然就能完成并發(fā)了。“
“這個方法聽起來才正常嘛,我馬上就回去試試,謝謝你了!”
希望你在看完我的文章之后有所收獲~(求點贊吶?。?/p>