[英]how to make a thread-safe global counter in python
不確定你是否已經嘗試過這種特定的語法,但對我來說這一直都很好用:
定義全局鎖:
import threading
threadLock = threading.Lock()
然后,每次增加單個線程中的計數器時,您必須獲取並釋放鎖定:
with threadLock:
global_counter += 1
最簡單的解決方案是使用multiprocessing.Lock
保護計數器。 multiprocessing.Lock
。 我喜歡將它保存在一個類中,如下所示:
from multiprocessing import Process, RawValue, Lock
import time
class Counter(object):
def __init__(self, value=0):
# RawValue because we don't need it to create a Lock:
self.val = RawValue('i', value)
self.lock = Lock()
def increment(self):
with self.lock:
self.val.value += 1
def value(self):
with self.lock:
return self.val.value
def inc(counter):
for i in range(1000):
counter.increment()
if __name__ == '__main__':
thread_safe_counter = Counter(0)
procs = [Process(target=inc, args=(thread_safe_counter,)) for i in range(100)]
for p in procs: p.start()
for p in procs: p.join()
print (thread_safe_counter.value())
上面的代碼中首先從禮Bendersky的博客采取這里 。
如果您使用的是 CPython 1 ,則無需顯式鎖定即可執行此操作:
import itertools
class Counter:
def __init__(self):
self._incs = itertools.count()
self._accesses = itertools.count()
def increment(self):
next(self._incs)
def value(self):
return next(self._incs) - next(self._accesses)
my_global_counter = Counter()
我們需要兩個計數器:一個用於計算增量,一個用於計算value()
的訪問次數。 這是因為itertools.count
不提供訪問當前值的方法,只提供下一個值。 因此,我們需要通過詢問值來“撤消”我們招致的增量。
這是線程安全的,因為itertools.count.__next__()
在 CPython 中是原子的(感謝 GIL。)並且我們不會保留差異。
請注意,如果並行訪問value()
,則確切的數字可能不會完全穩定或嚴格單調遞增。 它可以加上或減去與訪問的線程數成比例的余量。 理論上, self._incs
可以先在一個線程中更新,而self._accesses
可以先在另一個線程中更新。 但總的來說,系統永遠不會因為無人看守的寫入而丟失任何數據; 它總是會穩定到正確的值。
1並非所有 Python 都是 CPython,但很多(大多數?)是。
2感謝 https://julien.danjou.info/atomic-lock-free-counters-in-python/最初的想法是使用itertools.count
來遞增並使用第二個訪問計數器來更正。 他們幾乎沒有打開所有的鎖就停下了。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.