簡體   English   中英

使用ManualResetEvent可能出現的競賽情況

[英]Possible Race condition with ManualResetEvent

問題:

我試圖從ThreadPool拋出6個線程來處理單個任務。 每個任務的ManualResetEvent存儲在一組手動重置事件中。 線程數與ManualResetEvent數組中的索引相對應。

現在發生的事情是,一旦啟動了這6個線程,我就會移出並等待線程完成。 等待線程在主線程中完成。

現在有時候會發生的事情是,即使經過很長一段時間(我看到的兩天),我的等待邏輯也沒有返回。 這是線程等待邏輯的代碼示例

                foreach (ManualResetEvent whandle in eventList)
                {
                    try
                    {
                        whandle.WaitOne();
                    }
                    catch (Exception) { }
                }

根據.WaitOne的文檔。 這是同步調用,如果未從線程接收到Set事件,則使線程不返回。

有時,我的線程工作量較少,甚至可能在我到達“等待”邏輯之前返回。 即使過去收到過.WaitOne()是否也可能會等待Set()事件? 這是等待所有線程關閉的正確邏輯嗎?

(注意:我認為最好的選擇是Parallel.Invoke() -參見此答案的后面。)

您正在執行的操作通常都可以正常工作,因此問題可能出在您的線程之一由於某種原因被阻塞了。

您應該能夠足夠容易地調試它-您可以連接調試器並進入程序,然后查看調用堆棧以查看哪些線程被阻塞。 不過,如果您發現比賽狀況,請做好准備以防萬一!

請注意,您不能執行以下操作:

myEvent.Set();
myEvent.Reset();

.Set().Reset()之間沒有任何內容(或很少.Reset() 如果在myEvent等待多個線程時執行此操作,則其中一些線程將錯過正在設置的事件! (這種效果在MSDN上沒有得到很好的記錄。)

順便說一句,您不應忽略異常-至少始終以某種方式記錄異常。


(本節未回答問題,但可能會提供一些有用的信息)

我還想提到一種等待線程的替代方法。 由於您具有一組ManualResetEvents,因此可以將它們復制到純數組並將其傳遞給WaitHandle.WaitAll()

您的代碼可能看起來像這樣:

WaitHandle.WaitAll(eventList.ToArray());

等待所有線程完成的另一種方法是使用CountdownEvent 當倒數到零時,它會發出信號。 您從線程數開始計數,並且每個線程在退出時都會發出信號。 這里有一個例子在這里

Parallel.Invoke()

如果您的線程不返回值,而您只想啟動它們,然后讓啟動線程等待它們退出,那么我認為Parallel.Invoke()將是最好的方法。 它避免了您必須自己處理同步。

(否則,如svick在上面的評論中所述,請使用Task而不是舊的線程類。)

我沒有直接回答這個問題。 這是您應該做的:

使用Task.Factory.StartNew啟動任務,然后使用Task.WaitAll(Task[])等待Task.WaitAll(Task[]) 您不必以這種方式處理事件。 異常會很好地傳播到“ forking”線程。 您不再需要舊的ThreadPool API。

希望這可以幫助。

暫無
暫無

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

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