我正在重溫Marlow 書中的 STM 章節。在那里,它指出:
當多個執行緒阻塞在一個 上時
MVar,保證它們以先進先出的順序被喚醒
但是,在 STM 情況下不能這樣做:
事務可以在任意條件下阻塞,因此運行時不知道任何單個事務在
TVar更改后是否能夠取得進展;它必須運行事務才能找出答案。因此,當有多個事務可能被解除阻塞時,我們必須運行它們;畢竟,他們現在說不定都可以繼續了。
我不明白的是為什么由此得出
因為運行時必須運行所有被阻塞的事務,所以不能保證執行緒會按 FIFO 順序解除阻塞......
我希望即使我們必須在 STM 塊中運行所有事務,我們仍然可以按 FIFO 順序喚醒執行緒。所以我想我錯過了一些重要的細節。
uj5u.com熱心網友回復:
STM 的重點是推測:我們嘗試運行所有事務,希望它們不會彼此沖突(或執行 a retry)。當我們確實發現沖突時,我們允許一些事務提交,同時使沖突的事務回滾。
我們可以只運行一個事務,等待它完成或阻塞,然后運行另一個,依此類推,但這樣做相當于使用一個“全域鎖”,使計算完全按順序進行。
從技術上講,當執行緒等待 a 時MVar,這些執行緒將在一個非常簡單的條件下進行:MVar變為非空。喚醒時,執行緒將獲取該值,使其為空。因此,最多只有一個執行緒可以執行 take,并且喚醒多個執行緒毫無意義。
相比之下,當執行緒因 STM 而等待時,情況要復雜得多。假設他們正在等待,因為他們之前執行了 a retry,因此他們正在等待一些先前讀取的TVar更改。當這種情況發生時,除非我們重新運行它的事務,否則我們無法真正知道哪個執行緒會再次阻塞。與 不同MVar,現在有可能將它們全部喚醒會使它們全部完成而不會發生沖突,因此我們嘗試這樣做。在這樣做時,我們希望許多(如果不是全部)完成,并準備為那些沒有完成的人再次回滾。
考慮這個具體的 STM 示例:
Thread 1:
read X
if X is zero, retry
compute f(X)
write Y = f(X)
Thread 2:
read X
if X is zero, retry
compute g(X)
write Z = g(X)
假設一開始我們有“X=Y=Z=0”。我們運行兩個執行緒,但它們會重試。然后第三個執行緒寫入X=1. 我們可以喚醒兩者,并且按 FIFO 順序這樣做是沒有意義的,因為兩者都會完成。我們可以,而且應該,計算f(X)和g(X)并行。
現在,如果執行緒 2 寫在Y而不是Z最后,就會發生沖突。在這種情況下,按順序運行事務會更有效(否則我們計算f(X)或g(X)兩次)。但是,在一般情況下,很難預測是否會發生沖突。STM 不會嘗試,讓程式員來優化他們的代碼。
uj5u.com熱心網友回復:
當一堆執行緒正在等待從 an 中取出MVar并且有人填充它時,只有一個等待執行緒可以運行。這就是重點。喚醒的執行緒(大概)在完成其任務后會再喚醒一個執行緒并MVar依次填充執行緒。因此,有一個明確的順序讓服務員輪到他們是有道理的。
TVars 不是MVars。他們沒有空/保留與完整/可用的二分法。它們只是可以通過事務訪問和/或更改的值。執行緒不會“阻塞等待TVar”;他們只是讀取 的當前值TVar,然后決定他們無法在該值上取得進展(等等retry)。
運行時系統知道哪個TVar執行緒被訪問了,所以它知道不會再次喚醒那個執行緒,除非至少有一個TVars 發生了變化;對于純計算,執行緒的決定retry應該是TVar它讀取的所有s的純函式,所以我們知道它無法取得進展,直到其中一個發生變化。
但是當你有一堆執行緒在等待retry,它們都讀取相同的內容TVar并且它發生變化時,運行時系統不想只是喚醒一個。沒有理由相信只有其中一個能夠使用新值取得進展。STM 交易應該并行、樂觀地運行。我們想把他們都叫醒,讓他們試著逃跑。如果它們發生沖突,我們稍后會發現,通過檢測 STM 事務之間的爭用的正常方法;我們無法從他們都想閱讀相同的內容這一事實中看出這一點TVar。
我們可能會同時喚醒它們,并且(如果可用計算單元太多)嘗試確保調度程式開始以 FIFO 順序運行它們。也許這已經完成了;我不知道。但我猜想它們的處理方式與任何其他可供調度的執行緒完全相同,并且不采取任何特殊操作。它們“應該”全部并行運行,因此哪個先出閘并不重要。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/387035.html
上一篇:monad定義中的樣板代碼
