簡體   English   中英

死簡單示例中的線程鎖定失敗

[英]Thread locking failing in dead-simple example

這是最簡單的玩具示例。 我知道 concurrent.futures 和更高級別的代碼; 我選擇玩具示例是因為我正在教授它(作為與高級內容相同的材料的一部分)。

它從不同的線程踩到櫃台,我得到......好吧,這里更奇怪。 通常我得到的計數器比我應該得到的要少(例如 5M),通常比 20k 少得多。 但是當我減少循環次數時,像 1000 這樣的數字始終是正確的。 然后,在某個中間數,我得到差不多吧,偶爾正確的,但過一段時間比nthread X NLOOP個產品略大 在Jupyter細胞反復運行它,但第一行真的應該重新設置為零,不保留任何老總。

lock = threading.Lock()
counter, nthread, nloop = 0, 100, 50_000 

def increment(n, lock):
    global counter
    for _ in range(n):
        lock.acquire()
        counter += 1
        lock.release()

for _ in range(nthread):
    t = Thread(target=increment, args=(nloop, lock))
    t.start()
    
print(f"{nloop:,} loops X {nthread:,} threads -> counter is {counter:,}")

如果我添加.join()行為會改變,但仍然不正確。 例如,在不嘗試鎖定的版本中:

counter, nthread, nloop = 0, 100, 50_000 

def increment(n):
    global counter
    for _ in range(n):
        counter += 1

for _ in range(nthread):
    t = Thread(target=increment, args=(nloop,))
    t.start()
    t.join()
    
print(f"{nloop:,} loops X {nthread:,} threads -> counter is {counter:,}")
# --> 50,000 loops X 100 threads -> counter is 5,022,510

確切的超額計數各不相同,但我反復看到類似的情況。

我真的不想在鎖定示例中使用.join() ,因為我想說明后台作業的想法。 但是我可以等待線程的活躍度(謝謝 Frank Yellin!),這樣就解決了鎖問題。 不過,超額計算仍然困擾着我。

在查看counter之前,您不會等到所有線程都完成。 這也是您如此迅速地獲得結果的原因。

    threads = []
    for _ in range(nthread):
        t = threading.Thread(target=increment, args=(nloop, lock))
        t.start()
        threads.append(t)

    for thread in threads:
        thread.join()

    print(f"{nloop:,} loops X {nthread:,} threads -> counter is {counter:,}")

打印出預期的結果。

50,000 loops X 100 threads -> counter is 5,000,000

更新。 我強烈建議改用 ThreadPoolExecutor(),它會為您跟蹤線程。

    with ThreadPoolExecutor() as executor:
        for _ in range(nthread):
            executor.submit(increment, nloop, lock)
    print(...)

會給你你想要的答案,並負責等待線程。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM