Python在內置的threading
模塊中提供了Lock
類,該類相當於互斥鎖
,可以保護各線程數據結構不被破壞。
案例:新建一個Counter
類,統計傳感器採樣獲得的樣本數量。
### 計數器
class Counter(object):
def __init__(self):
self.count = 0
def increment(self,offset):
self.count += offset
### 傳感器
def woker(sensor_index,how_many,counter):
for _ in range(how_many):
## Read from the sensor
##....
counter.increment(1)
#### run_threads函數
def run_threads(func,how_many,counter):
threads = []
for i in range(5):
args = (i,how_many,counter)
thread = Thread(target=func,args = args)
threads.append(thread)
thread.start()
for thread in threads:
thread.joint()
if __name__=='__main__':
###執行
how_many = 10**5
counter = Counter()
run_threads(worker,how_many,counter)
print(5*how_many,counter.count)
輸出結果:
500000 278328
可以看出,兩者數據相差較大,這是由於程序在執行self.count += offset
語句時,並非原子操作,而是可以分解爲:
value = getattr(counter,'count')
result = value + offset
setattr(counter,'count',result)
上述三個操作,在任意兩個操作之間都可能發生線程切換,這種頻繁的線程切換,交錯執行可能會令線程把舊的value
設置給Counter
。
因此,在不加鎖的前提下,允許多條線程修改同一對象,那麼程序的數據結構可能會遭到破壞。
我們可以用互斥鎖
保護Counter
對象,使得多個線程同時訪問value
值的時候,不會將該值破壞。同一時刻,只有一個線程能夠獲得這把鎖。如下:
class LockingCounter(object):
def __init__(self):
self.lock = Lock()
self.count = 0
def increment(self,offset):
with self.lock:###判斷鎖狀態
self.count += offset
if __name__ == '__main__':
counter = LockingCounter()
run_threads(worker,how_many,counter)
print(5*how_many,counter.count)