Python中的鎖


一、全局解釋器鎖(GIL)

  1、什么是全局解釋器鎖

      在同一個進程中只要有一個線程獲取了全局解釋器(cpu)的使用權限,那么其他的線程就必須等待該線程的全局解釋器(cpu)使用權消失后才能使用全局解釋器(cpu),即時多個線程直接不會相互影響在同一個進程下也只有一個線程使用cpu,這樣的機制稱為全局解釋器鎖(GIL)。

  2、全局解釋器鎖的好處

      1、避免了大量的加鎖解鎖的好處

      2、使數據更加安全,解決多線程間的數據完整性和狀態同步

  3、全局解釋器的缺點

      多核處理器退化成單核處理器,只能並發不能並行。


同一時刻的某個進程下的某個線程只能被一個cpu所處理,所以在GIL鎖下的線程只能被並發,不能被並行。

實例:
import time
import threading


def sub():
    global num
    num -= 1
    time.sleep(1)
num = 100  # 定義一個全局變量
l = []  # 定義一個空列表,用來存放所有的列表
for i in range(100):  # for循環100次
    t = threading.Thread(target=sub)  #每次循環開啟一個線程
    t.start()  # 開啟線程
    l.append(t)  # 將線程加入列表l
for i in l:
    i.join()  # 這里加上join保證所有的線程結束后才運行下面的代碼
print(num)
# 輸出結果為0

二、同步鎖

  1、什么是同步鎖?

    同一時刻的一個進程下的一個線程只能使用一個cpu,要確保這個線程下的程序在一段時間內被cpu執,那么就要用到同步鎖。

  2、為什么用同步鎖?

    因為有可能當一個線程在使用cpu時,該線程下的程序可能會遇到io操作,那么cpu就會切到別的線程上去,這樣就有可能會影響到該程  序結果的完整性。

  3、怎么使用同步鎖?

    只需要在對公共數據的操作前后加上上鎖和釋放鎖的操作即可。

  4、實例:

import time
import threading

R = threading.Lock()


def sub():
    global num
    R.acquire() # 加鎖,保證同一時刻只有一個線程可以修改數據
    num -= 1
    R.release() # 修改完成就可以解鎖
    time.sleep(1)


num = 100  # 定義一個全局變量
l = []  # 定義一個空列表,用來存放所有的列表
for i in range(100):  # for循環100次
    t = threading.Thread(target=sub)  # 每次循環開啟一個線程
    t.start()  # 開啟線程
    l.append(t)  # 將線程加入列表l
for i in l:
    i.join()  # 這里加上join保證所有的線程結束后才運行下面的代碼
print(num)
# 輸出結果為0

  5、擴展知識

    1、GIL的作用:多線程情況下必須存在資源的競爭,GIL是為了保證在解釋器級別的線程唯一使用共享資源(cpu)。

    2、同步鎖的作用:為了保證解釋器級別下的自己編寫的程序唯一使用共享資源產生了同步鎖。

三、遞歸鎖和死鎖

  1、什么是死鎖?

    指兩個或兩個以上的線程或進程在執行程序的過程中,因爭奪資源而相互等待的一個現象

import time
import threading

A = threading.Lock()
B = threading.Lock()
import threading


class obj(threading.Thread):
    def __init__(self):
        super().__init__()

    def run(self):
        self.a()   # 如果兩個鎖同時被多個線程運行,就會出現死鎖現象
        self.b()
    def a(self):
        A.acquire()
        print('123')
        B.acquire()
        print(456)
        time.sleep(1)
        B.release()
        print('qweqwe')
        A.release()
    def b(self):
        B.acquire()
        print('asdfaaa')
        A.acquire()
        print('(⊙o⊙)哦(⊙v⊙)嗯')
        A.release()
        B.release()
for i in range(2):  # 循環兩次,運行四個線程,第一個線程成功處理完數據,第二個和第三個就會出現死鎖
    t = obj()
    t.start()

程序會出現阻塞現象

 

 2、什么是遞歸鎖?

    在Python中為了支持同一個線程中多次請求同一資源,Python提供了可重入鎖。這個RLock內部維護着一個Lock和一個counter

  變量,counter記錄了acquire的次數,從而使得資源可以被多次require。直到一個線程所有的acquire都被release,其他的線程才能獲

  得資源。

import time
import threading

A = threading.RLock()  # 這里設置鎖為遞歸鎖
import threading


class obj(threading.Thread):
    def __init__(self):
        super().__init__()

    def run(self):
        self.a()
        self.b()
  
    def a(self): # 遞歸鎖,就是將多個鎖的鑰匙放到一起,要拿就全拿,要么一個都拿不到
                # 以實現鎖
        A.acquire()
        print('123')
        print(456)
        time.sleep(1)
        print('qweqwe')
        A.release()
    def b(self):
        A.acquire()
        print('asdfaaa')
        print('(⊙o⊙)哦(⊙v⊙)嗯')
        A.release()
for i in range(2):
    t = obj()
    t.start()

四、信號量(semaphore)

  1、什么是信號量?

    同進程的一樣,semaphore管理一個內置的計數器,每當調用acquire()時內置函數-1,每當調用release()時內置函數+1。

   計數器不能為0,當計數器為0時acquire()將阻塞線程,直到其他線程調用release()。

import threading
import time

mysf = threading.Semaphore(5)  # 創建信號量對象,(5表示這個鎖同時支持的個數)


def func():
    if mysf.acquire():  # 因為使用了信號量,下面的輸出就會5個5個的同時輸出
        print(threading.currentThread().getName() + 'get semaphore')  
        time.sleep(1)
        mysf.release()
for i in range(20):
    t = threading.Thread(target=func)
    t.start()

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM