GIL全局解釋器鎖及協程

GIL全局解釋器鎖

一、什麼是GIL全局解釋器鎖

GIL本質是一把互斥鎖,至關於執行權限,每一個進程內都會存在一把GIL同一進程內的多線程,必須搶到GIL以後才能使用Cpython解釋器來執行本身的代碼,即同一進程下的多個線程沒法實現並行,但能夠實現併發python

Cpython解釋器下想實現並行能夠開啓多個進程安全

二、爲什麼要有GIL

由於Cpython解釋器的垃圾回收機制不是線程安全的,保證了數據的安全多線程

三、GIL全局解釋器的優缺點

優勢:保證了數據安全併發

缺點:單個進程下開啓多個線程只能實現併發不能實現並行app

四、選擇多進程仍是多線程?

單核或多核I/O密集型下使用:多線程性能

單核計算密集型下使用:多線程測試

多核計算密集型下使用:多進程spa

多cpu,意味着能夠有多個核並行完成計算,因此多核提高的是計算性能操作系統

每一個cpu一旦遇到I/O阻塞,仍然須要等待,因此多核對I/O操做沒什麼用處線程

對於一個程序來講不多是純IO型或者純計算型,咱們只能相對的判斷是IO密集型仍是計算密集型,來選擇多進程仍是多線程

單核狀況下:

​ 開啓四個任務是計算密集型的,沒有多核來並行計算,開多個進程,只是徒增進程的開銷內存資源,應該使用多線程

​ 開啓四個任務是IO密集型的,開啓多進程也是徒增,來回切換的速度還不如開啓多線程的,因此應該使用多線程

多核狀況下:

​ 開啓四個任務是計算密集型的,多核意味着多個CPU去計算,開啓多進程能夠同一時刻去計算,因此應該使用多進程

​ 開啓四個任務是IO密集型的,再多的核也在在等待,來回切換的速度進程不如線程的,因此應該使用多線程

五、多線程多進程多核下性能測試

from multiprocessing import Process
from threading import Thread
import os
import time


# 計算密集型
def task1():
    i = 0
    for line in range(110000000):
        i += 1

if __name__ == '__main__':
    print(os.cpu_count())  # 查看計算機幾核的
    list1 = []
    start_time = time.time()
    for i in range(4):
        p1 = Process(target=task1)  # 4進程下: 17.369488954544067 
        # t1 = Thread(target=task1) # 4線程下: 27.991361618041992
        p1.start()
        # t1.start()
        # list1.append(t1)
        list1.append(p1)
    for i in list1:
        i.join()
    end_time = time.time()
    print(end_time - start_time)


# IO密集型
def task2():
    time.sleep(1)

if __name__ == '__main__':
    print(os.cpu_count())
    start_time = time.time()
    list2 = []
    for i in range(40):
        # p2 = Process(target=task2) #40進程下: 11.374541282653809
        t2 = Thread(target=task2)  # 40線程下: 1.010239839553833
        # p2.start()
        t2.start()
        # list2.append(p2)
        list2.append(t2)
    for i in list2:
        i.join()
    end_time = time.time()
    print(end_time - start_time)

協程

一、什麼是協程

進程:資源單位

線程:執行單位

協程:單線程下實現併發

在I/O密集型的狀況下,使用協程提升執行效率

手動的實如今同一線程下 「 遇到I/O切換+保存狀態 」 讓操做系統誤覺得沒有I/O操做,將CPU執行權限繼續交給你

即:在單線程下實現多個任務遇到IO就切換能夠下降單線程的IO時間,從而最大限度的提高單線程的效率

二、實現協程

gevent模塊:遇到I/O自動切換並保存狀態

使用gevent模塊中的monkey,monkey.patch_all()來監視是否遇到IO操做,再使用spawn來建立協程,使用joinall替代join,使協程運行完再結束線程,joinall中放入獲得的對象到列表中

from gevent import spawn
from gevent import joinall
from gevent import monkey
import time

# 補丁:監聽全部的任務是否有IO操做
monkey.patch_all()


def task1(name):
    print(f'{name}開始')
    time.sleep(1)
    print(f'{name}結束')


def task2():
    print('task2開始')
    time.sleep(3)
    print('task2結束')


def task3():
    print('task3開始')
    time.sleep(5)
    print('task3結束')


if __name__ == '__main__':
    start_time = time.time()
    # 建立協程
    sp1 = spawn(task1,'task1')
    sp2 = spawn(task2)
    sp3 = spawn(task3)
    # sp1.join()
    # sp2.join()
    # sp3.join()
    joinall([sp1, sp2, sp3]) # 至關於 sp.join(),注意放入列表中
    end_time = time.time()
    print(end_time - start_time)
    # task1開始
    # task2開始
    # task3開始
    # task1結束
    # task2結束
    # task3結束
    # 5.013161897659302
相關文章
相關標籤/搜索