Python的GIL:全局解釋鎖(Global Interpreter Lock)在執行多線程時,同一時刻至多隻有一個線程被CPU執行,如果要實現並行可以使用多進程。

Python適用於IO密集型情況,不太適用於計算密集型的場景,因爲執行計算時線程間的切換會耗費時間。

下面有三種情況

import threading
import time
def sub():
    global num
    temp=num
    print('A',end='|')
    num=temp-1
    print('B',end='|')

num=30
l=[]
for i in range(30):
    t=threading.Thread(target=sub)
    l.append(t)
    t.start()

for t in l:
    t.join()

print('\n',num)

#結果爲0
import threading
import time
def sub():
    global num
    temp=num
    print('A',end='|')
    time.sleep(1)
    num=temp-1
    print('B',end='|')

num=30
l=[]
for i in range(30):
    t=threading.Thread(target=sub)
    l.append(t)
    t.start()

for t in l:
    t.join()

print('\n',num)

#結果爲29
import threading
import time
def sub():
    global num
    temp=num
    print('A',end='|')
    time.sleep(0.001)
    num=temp-1
    print('B',end='|')

num=30
l=[]
for i in range(30):
    t=threading.Thread(target=sub)
    l.append(t)
    t.start()

for t in l:
    t.join()

print('\n',num)

#每次的結果都不同

第一種情況,因爲程序運行時間太短,所以線程間不用切換,相當於線程一個一個執行每次都減1故結果爲0,從輸出的AB情況也可以看出

第二種情況,當碰到sleep時解釋器會進行線程的任務切換,而且sleep1秒的時間過長,導致每個線程的temp都拿到num的初始值30,然後全部進行減1操作將結果29又賦給num,所以最好num等於29,從輸出的AB情況也可以看出

第三種情況,碰到sleep時開始切換線程,但是由於sleep時間短,只有部分線程temp拿到num的初始值30,然後程序又切回原來的線程進行減1運算,將結果賦給num,而後面的部分線程的temp獲得新的num值,而後又切換回來做減1運算,然後後面的線程獲得新的num值,如此往復直到結束。因爲每次切換的時間點並不唯一,所以最後的num值也不一樣。從AB的情況也可以看出。

要得到0的結果還可以用Lock鎖

import threading
import time

def sub():
    global num
    lock.acquire()#獲取鎖
    temp=num
    print('A',end='|')
    time.sleep(0.01)
    num=temp-1
    lock.release()#釋放鎖
    print('B',end='|')

lock=threading.Lock()

num=30
l=[]
for i in range(30):
    t=threading.Thread(target=sub)
    l.append(t)
    t.start()

for t in l:
    t.join()

print('\n',num)

在線程獲取鎖之後,只能執行鎖下面的程序,直到鎖被釋放,才能執行其他線程

死鎖的情況

import threading
import time
class MyThread(threading.Thread):
    def actionA(self):

        A.acquire()
        print(self.name,'gotA',time.ctime())
        time.sleep(2)

        B.acquire()
        print(self.name,'gotB',time.ctime())
        time.sleep(1)

        B.release()
        A.release()

    def actionB(self):

        B.acquire()
        print(self.name,'gotB',time.ctime())
        time.sleep(2)

        A.acquire()
        print(self.name,'gotA',time.ctime())
        time.sleep(1)

        A.release()
        B.release()

    def run(self):
        self.actionA()
        self.actionB()



if __name__ == '__main__':
    A=threading.Lock()
    B=threading.Lock()

    l=[]
    for i in range(5):
        t=MyThread()
        l.append(t)
        t.start()
    for t in l:
        t.join()

print('ending...')
結果爲
Thread-1 gotA Thu Apr 19 20:14:41 2018#線程1開始執行actionA,獲得A鎖
Thread-1 gotB Thu Apr 19 20:14:43 2018#線程1,獲得B鎖,並於1秒後釋放B鎖和A鎖
Thread-1 gotB Thu Apr 19 20:14:44 2018#線程1執行actionB,獲得B鎖,碰到sleep,進行線程切換
Thread-2 gotA Thu Apr 19 20:14:44 2018#線程2開始執行actionA,獲得A鎖
#線程1要繼續運行就要獲得A鎖,線程2要繼續運行就要獲得B鎖,但是鎖都到對方手中,所以造成程序無法繼續執行,就是死鎖

要解決以上問題可以用遞歸鎖

import threading
import time
class MyThread(threading.Thread):#用類來實現多線程,必須繼承threading.Thread,且必須定義run函數,因爲t.start()就是調用run函數
    def actionA(self):

        r_lock.acquire()#count=1
        print(self.name,'gotA',time.ctime())
        time.sleep(2)

        r_lock.acquire()#count=2
        print(self.name,'gotB',time.ctime())
        time.sleep(1)

        r_lock.release()#count=1
        r_lock.release()#count=0

    def actionB(self):

        r_lock.acquire()
        print(self.name,'gotB',time.ctime())
        time.sleep(2)

        r_lock.acquire()
        print(self.name,'gotA',time.ctime())
        time.sleep(1)

        r_lock.release()
        r_lock.release()

    def run(self):
        self.actionA()
        self.actionB()



if __name__ == '__main__':
    r_lock=threading.RLock()

    l=[]
    for i in range(5):
        t=MyThread()
        l.append(t)
        t.start()
    for t in l:
        t.join()

print('ending...')

遞歸鎖裏有個計數器,當遞歸鎖被acquire一次計數器就加一,release一次計數器就減1。

只有當計數器等於0時才能被其他的線程獲得,大於0時只能執行現在獲得鎖的線程,直到計數器再次變爲0時,其他線程纔有可能獲得遞歸鎖,解決了死鎖的情況

發佈了51 篇原創文章 · 獲贊 2 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章