概念梳理:
臨界區(qū):
臨界區(qū)指的是一個(gè)訪問共用資源(例如:共用設(shè)備或是共用存儲(chǔ)器)的程序片段
,而這些共用資源又無法同時(shí)被多個(gè)線程訪問的特性。當(dāng)有線程進(jìn)入臨界區(qū)段時(shí),其他線程或是進(jìn)程必須等待,有一些同步的機(jī)制必須在臨界區(qū)段的進(jìn)入點(diǎn)與離開點(diǎn)實(shí)現(xiàn),以確保這些共用資源是被互斥獲得使用。
目的:
我們?cè)诙嗑€程
處理是,經(jīng)常會(huì)涉及到對(duì)于一塊共享資源的訪問,這里就需要線程可以互斥去避免出現(xiàn)競(jìng)態(tài)條件(race condition).
方法:
要想讓可變對(duì)象安全地在多線程環(huán)境中進(jìn)行訪問,就要使用鎖變量(threading中的LockRLock),鎖變量也是同步原語
中的最常見一種之一。
示例:
import threading
class ShareCounter:
'''
一個(gè)可以在多線程中分享的類
'''
def __init__(self, initial_value=0):
self._value = initial_value
self._value_lock = threading.Lock()
def add_one(self, delta=1):
with self._value_lock:# with 上下文管理器,是的鎖的申請(qǐng)和釋放更加方便(自動(dòng)釋放),更好地保證了互斥
# 同樣很好地避免了死鎖
self._value += delta#執(zhí)行縮進(jìn)語句時(shí)獲得鎖,縮進(jìn)語句結(jié)束后自動(dòng)釋放鎖
def sub_one(self):
with self._value_lock:
self._value -= 1
#with管理器完全等同于如下操作(老版本的Python/ target=_blank class=infotextkey>Python中):
# self._value_lock.acquire()
# self._value -= 1
# self._value_lock.release()
線程的調(diào)度從本質(zhì)上來說是非確定性的(只能保證獨(dú)一訪問,但保證不了誰先誰后)。只要共享的可變狀態(tài)需要被多個(gè)線程訪問,就要使用鎖機(jī)制,保證數(shù)據(jù)的安全。
在threading庫中我們也發(fā)現(xiàn)了其他的同步原語例如:RLock(可重入鎖)、Semaphore對(duì)象(信號(hào)量)。
RLock:可以被同一個(gè)線程多次獲取,主要用來編寫基于鎖的代碼,或者基于‘監(jiān)聽器’的同步處理。
當(dāng)某個(gè)類持有這種類型鎖時(shí),只有一個(gè)線程可以使用類中全部函數(shù)或者方法,例如:
import threading
class ShareCounter:
'''
一個(gè)可以在多線程中分享的類
'''
_lock = threading.RLock()
def __init__(self, initial_value=0):
self._value = initial_value
def add_one(self, delta=1):
with ShareCounter._lock:
self._value += delta
def sub_one(self, delta=1):
with ShareCounter._lock:
self.add_one(-delta)
這份代碼中只有一個(gè)作用于整個(gè)類的鎖,它被所有的類實(shí)例所共享,不再將所綁定在某個(gè)實(shí)例的可變狀態(tài)上,現(xiàn)在這個(gè)鎖是用來同步類中的方法的。對(duì)于其他標(biāo)準(zhǔn)鎖不同的是,對(duì)于已經(jīng)持有了該鎖的方法可以調(diào)用同樣使用了這個(gè)鎖的其他方法(參考sub_one())。
這個(gè)實(shí)現(xiàn)的特點(diǎn)是,無論創(chuàng)建了多少counter實(shí)例,這些實(shí)例共有同一把鎖。因此,當(dāng)有大量counter出現(xiàn)時(shí),這種方法堆內(nèi)存的使用效率要高很多。但是可能存在的缺點(diǎn)是在使用了大量線程且需要頻繁更新counter中的數(shù)據(jù)時(shí),這么做會(huì)出現(xiàn)鎖爭(zhēng)用的情況。
另外一種同步原語semaphore,是一種基于共享計(jì)數(shù)器的同步原語。如果計(jì)數(shù)器非0,那么with語句會(huì)遞減計(jì)數(shù)器并且允許線程繼續(xù)執(zhí)行。當(dāng)with語句塊結(jié)束后,會(huì)將計(jì)數(shù)器遞增。如果計(jì)數(shù)器為0,那么執(zhí)行過程會(huì)被阻塞,直到由另外一個(gè)線程來遞增計(jì)數(shù)器為止。由于信號(hào)量
的實(shí)現(xiàn)更為復(fù)雜,這會(huì)對(duì)程序帶來一定的負(fù)面影響。除了簡(jiǎn)單地加鎖功能外,信號(hào)量對(duì)象對(duì)于那些設(shè)計(jì)在線程間發(fā)送信號(hào)或者需要實(shí)現(xiàn)節(jié)流處理的應(yīng)用中更加有用,例如限制并發(fā)總數(shù):
from threading import Semaphore
import urllib.request
_fetch_url_sema = Semaphore(5)
def fetch_url(url):
with _fetch_url_sema:
return urllib.request.urlopen(url)