我們有一些代碼可以創建一些BackgroundWorker線程,每個線程都執行一些數據庫工作。有時候這些線程會拋出一個異常(通常是由於超時 - 這是最近發生的事情,我不是那個必須弄清楚的人)。BackgroundWorker.RunWorker在List.Add中發生異常
如果任何線程失敗,整個操作就沒用了,整個事情都發生在Web服務調用中。因此,如果失敗,我們需要在主線程中拋出一個異常,該異常將被捕獲並轉換爲客戶端的SOAP錯誤異常。
我們收集列表中的線程例外。在幾十次的代碼中,多達7個工作線程都在同一時間拋出異常,有一次List在System.Collections.Generic.List`1.Add(T item)中拋出異常:
System.IndexOutOfRangeException
Message: Index was outside the bounds of the array.
這裏的,大致代碼:
// Collect Exceptions thrown by async calls.
var exAsync = new List<Exception>();
int ctThreadsFinished = 0;
int ctThreadsBegun = 0;
Action<Exception> handleException = (ex) => {
lock(exAsync) {
++ctThreadsFinished;
exAsync.Add(ex);
}
};
// ...create and run multiple BackgroundWorker threads, incrementing
// ctThreadsBegun for each thread. They will ++ctThreadsFinished on
// successful completion. That part works.
// If a thread throws an exception, its RunWorkerCompleted event will pass the
// exception to handleException.
while (ctThreadsFinished < ctThreadsBegun)
{
System.Threading.Thread.Sleep(100);
}
if (exAsync.Count == 1)
{
throw new Exception(exAsync.First().Message, exAsync.First());
}
else if (exAsync.Count > 1)
{
var msg = String.Join("\n", exAsync.Select(ex => ex.Message));
throw new AggregateException(msg, exAsync);
}
我把鎖就可以了,因爲我曾以爲,RunWorkerCompleted是在工作線程(稱爲其normally it isn't,但是這是Web服務,它看起來如behavior outside a Windows application will differ)。
例外看起來像像List.Add由線程1調用,然後在第一個調用仍在執行並且該對象仍處於不一致狀態時由線程2調用。由於多個線程會觸發默認的30秒SqlCommand超時,因此總是(實際上到目前爲止)出現多個失敗,他們將在同一時間執行此操作。如果在列表上沒有鎖定,我可以在一個小測試應用程序中重新創建該行爲,。
難道它是在加入之前增加ctThreadsFinished以恰好恰當的時刻來越過等待循環,所以它在Add()調用期間訪問exAsync.Count或exAsync.First()?可以破解Add()嗎?擁有一個共享鎖對象並且在等待循環中的計數器訪問周圍放置鎖定,並在末尾放置位,這當然是明智的。
但是,即使訪問exAsync的所有內容在主線程中都沒有這樣做,但在Add()調用周圍還是有一個lock()塊。我的第一個衝動是用System.Collections.Concurrent.ConcurrentBag替換List,但我沒有特別的理由相信會解決這個問題。
這對任何人都有意義嗎?
對,我鎖定的對象是任意的。可能是列表,可能是我母親在Unicode中的孃家姓。它鎖定了一段代碼,而不是列表本身:http://msdn.microsoft.com/en-us/library/c5kehkcz(v=vs.110).aspx我期望它工作,因爲exAsync.Add()是隻在該代碼段中調用,不是因爲我使用exAsync作爲鎖定標記。 –
是的,但後來訪問列表以檢查異常的代碼不在鎖內,所以即使最後一個異常被另一個線程添加,它也會高興地訪問您的列表。 – Jason
是的,的確如此。 –