天天看點

說說GIL

上一篇:線程深入篇引入

Code:

https://github.com/lotapp/BaseCode/tree/master/python/5.concurrent/Thread/3.GIL

說說GIL

盡管Python完全支援多線程程式設計, 但是解釋器的C語言實作部分在完全并行執行時并不是線程安全的,是以這時候才引入了GIL

解釋器被一個全局解釋器鎖保護着,它確定任何時候都隻有一個Python線程執行(保證C實作部分能線程安全) GIL最大的問題就是Python的多線程程式并不能利用多核CPU的優勢 (比如一個使用了多個線程的計算密集型程式隻會在一個單CPU上面運作)

注意:GIL隻會影響到那些嚴重依賴CPU的程式(比如計算型的)如果你的程式大部分隻會涉及到I/O,比如網絡互動,那麼使用多線程就很合适 ~ 因為它們大部分時間都在等待(線程被限制到同一時刻隻允許一個線程執行這樣一個執行模型。GIL會根據執行的位元組碼行數和時間片來釋放GIL,在遇到IO操作的時候會主動釋放權限給其他線程)

是以Python的線程更适用于處理

I/O

和其他需要并發執行的阻塞操作,而不是需要多處理器并行的計算密集型任務(對于IO操作來說,多程序和多線程性能差别不大)【計算密集現在可以用Python的

Ray

架構】

網上摘取一段關于

IO密集和計算密集

的說明:(IO密集型可以結合異步)

計算密集型任務的特點是要進行大量的計算,消耗CPU資源,比如計算圓周率、對視訊進行高清解碼等等,全靠CPU的運算能力。這種計算密集型任務雖然也可以用多任務完成,但是任務越多,花在任務切換的時間就越多,CPU執行任務的效率就越低,是以,要最高效地利用CPU,計算密集型任務同時進行的數量應當等于CPU的核心數。

計算密集型任務由于主要消耗CPU資源,是以,代碼運作效率至關重要。Python這樣的腳本語言運作效率很低,完全不适合計算密集型任務。對于計算密集型任務,最好用C語言編寫。

第二種任務的類型是IO密集型,涉及到網絡、磁盤IO的任務都是IO密集型任務,這類任務的特點是CPU消耗很少,任務的大部分時間都在等待IO操作完成(因為IO的速度遠遠低于CPU和記憶體的速度)。對于IO密集型任務,任務越多,CPU效率越高,但也有一個限度。常見的大部分任務都是IO密集型任務,比如Web應用。

IO密集型任務執行期間,99%的時間都花在IO上,花在CPU上的時間很少,是以,用運作速度極快的C語言替換用Python這樣運作速度極低的腳本語言,完全無法提升運作效率。對于IO密集型任務,最合适的語言就是開發效率最高(代碼量最少)的語言,腳本語言是首選,C語言最差。
           

Process and Thread Test

其實用不用多程序看你需求,不要麻木使用,Linux下還好點,Win下程序開銷就有點大了(好在伺服器基本上都是Linux,程式員開發環境也大多Linux了)這邊隻是簡單測了個啟動時間差距就來了,其他的都不用測試了

測試Code:

from time import sleep
from multiprocessing import Process

def test(i):
    sleep(1)
    print(i)

def main():
    t_list = [Process(target=test, args=(i, )) for i in range(1000)]
    for t in t_list:
        t.start()

if __name__ == '__main__':
    main()
           

運作時間:

real	0m3.980s
user	0m2.034s
sys	 0m3.119s
           

作業系統幾千個程序開銷還是有點大的(畢竟程序是有上線的)

ulimit -a

說說GIL
from time import sleep
from multiprocessing.dummy import Process

def test(i):
    sleep(1)
    print(i)

def main():
    t_list = [Process(target=test, args=(i, )) for i in range(1000)]
    for t in t_list:
        t.start()

if __name__ == '__main__':
    main()
           
real	0m1.130s
user	0m0.158s
sys	 0m0.095s
           

multiprocessing.dummy

裡面的Process上面也說過了,就是線上程基礎上加點東西使得用起來和

multiprocessing

Process

程式設計風格基本一緻(本質還是線程)

測試Code:

from time import sleep
from multiprocessing.dummy import threading

def test(i):
    sleep(1)
    print(i)

def main():
    t_list = [threading.Thread(target=test, args=(i, )) for i in range(1000)]
    for t in t_list:
        t.start()

if __name__ == '__main__':
    main()
           
real	0m1.123s
user	0m0.154s
sys	 0m0.085s
           

其實Redis就是使用單線程和多程序的經典,它的性能有目共睹。所謂性能無非看個人能否充分發揮罷了。不然就算給你轟炸機你也不會開啊?紮心不老鐵~

PS:線程和程序各有其好處,無需一棍打死,具體啥好處可以回顧之前寫的程序和線程篇~

利用共享庫來擴充

C系擴充

GIL是Python解釋器設計的曆史遺留問題,多線程程式設計,模型複雜,容易發生沖突,必須用鎖加以隔離,同時,又要小心死鎖的發生。Python解釋器由于設計時有GIL全局鎖,導緻了多線程無法利用多核。計算密集型任務要真正利用多核,除非重寫一個不帶GIL的解釋器(

PyPy

)如果一定要通過多線程利用多核,可以通過C擴充來實作(Python很多子產品都是用C系列寫的,是以用C擴充也就不那麼奇怪了)

隻要用C系列寫個簡單功能(不需要深入研究高并發),然後使用

ctypes

導入使用就行了:

#include <stdio.h>  

void test()  
{  
  while(1){}
}
           

編譯成共享庫:

gcc 2.test.c -shared -o libtest.so

說說GIL

使用Python運作指定方法:(

太友善了,之前一直以為C#調用C系列最友善,用完Python才知道更簡方案

from ctypes import cdll
from os import cpu_count
from multiprocessing.dummy import Pool

def main():
    # 加載C共享庫(動态連結庫)
    lib = cdll.LoadLibrary("./libtest.so")

    pool = Pool()  # 預設是系統核數
    pool.map_async(lib.test, range(cpu_count()))
    pool.close()
    pool.join()

if __name__ == '__main__':
    main()
           

看看這時候HTOP的資訊:(充分利用多核)【ctypes在調用C時會自動釋放GIL】

說說GIL

Go擴充

利用Go寫個死循環,然後編譯成so動态連結庫(共享庫):

package main
import "C"

//export test
func test(){
	for true{
    }
}

func main() {
	test()
}
           

非常重要的事情:

//export test

一定要寫,不然就被自動改成其他名字(我當時被坑過)

Python調用和上面一樣:

from ctypes import cdll
from os import cpu_count
from multiprocessing.dummy import Pool

def main():
    # 加載動态連結庫
    lib = cdll.LoadLibrary("./libtestgo.so")

    pool = Pool()  # 預設是系統核數
    pool.map_async(lib.test, range(cpu_count()))
    pool.close()
    pool.join()

if __name__ == '__main__':
    main()
           

效果:

go build -buildmode=c-shared -o libtestgo.so 2.test.go

說說GIL

題外話~如果想等CPython的GIL消失可以先看一個例子:MySQL把大鎖改成各個小鎖花了5年。在是在MySQL有專門的團隊和公司前提下,而Python完全靠社群重構就太慢了

速度方面微軟除外,更新快本來是好事,但是動不動斷層更新,這學習成本就太大了(這也是為什麼Net能深入的人比較少的原因:人家剛深入一個,你就淘汰一個了...)

可能還有人不清楚,貼下官方推薦技術吧(

NetCore

Orleans

EFCore

ML.Net

CoreRT

https://github.com/aspnet/AspNetCore

https://github.com/aspnet/EntityFrameworkCore

https://github.com/dotnet/machinelearning

https://github.com/dotnet/orleans

https://github.com/aspnet/Mvc

https://github.com/dotnet/corert
           

課外拓展:

用go語言給python3開發子產品
https://www.jianshu.com/p/40e069954804
https://blog.filippo.io/building-python-modules-with-go-1-5

Python與C/C++互相調用
https://www.cnblogs.com/apexchu/p/5015961.html

使用C/C++代碼編寫Python子產品
https://www.cnblogs.com/silvermagic/p/9087896.html

快速實作python c擴充子產品
https://www.cnblogs.com/chengxuyuancc/p/6374239.html

Python的C語言擴充
https://python3-cookbook.readthedocs.io/zh_CN/latest/chapters/p15_c_extensions.html

python調用golang生成的so庫
https://studygolang.com/articles/10228
https://www.cnblogs.com/huangguifeng/p/8931837.html

python調用golang并回調
https://blog.csdn.net/gtd138/article/details/79801235

Python3.x AttributeError: libtest.so: undefined symbol: fact
https://www.cnblogs.com/tanglizi/p/8965230.html
           

運作在其他編譯器上

先看最重要的一點,一旦運作在其他編譯器意味着很多Python第三方庫

可能

就不能用了,相對來說

PyPy

相容性是最好的了

如果是

Python2

系列我推薦谷歌的

grumpy
Grumpy是一個 Python to Go 源代碼轉換編譯器和運作時。旨在成為CPython2.7的近乎替代品。關鍵的差別在于它将Python源代碼編譯為Go源代碼,然後将其編譯為本機代碼,而不是位元組碼。這意味着Grumpy沒有VM

已編譯的Go源代碼是對Grumpy運作時的一系列調用,Go庫提供與 Python C API類似的目的
           

Python3

系列,可以使用

PyPy

PythonNet

Jython3

ironpython3

等等

PyPy:https://bitbucket.org/pypy/pypy

Net方向:

https://github.com/pythonnet/pythonnet
https://github.com/IronLanguages/ironpython3
           

Java方向:

https://github.com/jython/jython3
           

Other:

源碼:https://github.com/sbinet/go-python
參考:https://studygolang.com/articles/13019

可惜CoreRT一直沒完善,不然就Happy了
https://github.com/dotnet/corert
           

經驗

:平時基本上多線程就夠用了,如果想多核利用-多程序基本上就搞定了(分布式走起)實在不行一般都是分析一下性能瓶頸在哪,然後寫個擴充庫

如果需要和其他平台互動才考慮上面說的這些項目。如果是Web項目就更不用擔心了,現在哪個公司還不是混用?

JavaScript and Python and Go or Java or NetCore

。基本上上點規模的公司都會用到Python,之前都是

Python and Java

搭配使用,這幾年開始慢慢變成

Python and Go or NetCore

搭配使用了~

下集預估:

Actor模型

and

消息釋出/訂閱模型

作者:毒逆天

出處:https://www.cnblogs.com/dotnetcrazy

打賞:

18i4JpL6g54yAPAefdtgqwRrZ43YJwAV5z

本文版權歸作者和部落格園共有。歡迎轉載,但必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接!