[英]What happens when multiple parallel threads await the same Task instance which then throws?
通讀這個問題的答案,促使我思考當等待的任務拋出時異常情況會如何發生。 所有“客戶”都能觀察到異常嗎? 我承認我可能會在這里混淆兩件事。 這就是我要求澄清的原因。
我將給出一個具體的場景...假設我有一台服務器,其中包含由客戶端啟動的長期運行的Task
實例的全局集合。 啟動一項或多項任務后,客戶端可以查詢其進度並在可用時檢索結果以及可能發生的任何錯誤。
任務本身可以執行非常不同的業務特定的事情-通常,沒有兩個是完全相同的。 但是,如果其中一個客戶端確實嘗試啟動與先前已啟動的另一個客戶端相同的任務,則服務器應識別出該問題並將第二個客戶端“附加”到現有任務,而不是假脫機處理新副本。
現在,任何客戶端每次查詢其感興趣的任務的狀態時,都會按照以下步驟進行操作:
var timeout = Task.Delay(numberOfSecondsUntilClientsPatienceRunsOut);
try
{
var result = await Task.WhenAny(timeout, task);
if (result == timeout)
{
// SNIP: No result is available yet. Just report the progress so far.
}
// SNIP: Report the result.
}
catch (Exception e)
{
// SNIP: Report the error.
}
簡而言之,它為任務提供了一些合理的時間來首先完成其工作,然后退回報告正在進行的進度。 這意味着可能存在很長的時間窗口,在此時間內多個客戶端可能會觀察到同一任務失敗。
我的問題是:如果任務恰好在此窗口期間拋出,所有客戶端是否都觀察到(並處理了)異常?
Task.WhenAny
不會自行拋出。 根據文檔 :
當提供的任何任務完成時,返回的任務將完成。 返回的任務將始終以
RanToCompletion
狀態結束,其Result設置為要完成的第一個任務。 即使第一個要完成的任務以“Canceled
或“Faulted
狀態結束,也是如此。
您將返回timeout
或task
。
如果結果為task
,並且您等待該結果(或獲取Task.Result
),並且task
發生故障,則將拋出該錯誤。 無論有多少調用者執行此操作,或何時執行此操作都無關緊要-總是嘗試獲取錯誤任務的結果。 簡單的代碼演示這一點:
var t = Task.Run(() => throw new Exception(DateTime.Now.Ticks.ToString()));
try {
await t;
} catch (Exception e) {
Console.WriteLine(e.Message);
}
await Task.Delay(1000);
try {
await t;
} catch (Exception e) {
Console.WriteLine(e.Message);
}
這將打印相同的時間戳兩次。 該任務只運行一次,只有一個結果,每次嘗試獲取它都會產生一個異常。 如果您喜歡可以混入不同的線程或並行調用,則不會改變結果。
請注意,在超時的情況下,仍然存在爭用條件的基本可能性:等待同一任務的兩個不同的任務/線程在await Task.WhenAny(timeout, task)
可能會得到不同的結果。他們觀察要首先完成的任務。 換句話說,即使await Task.WhenAny(timeout, task) == timeout
, task
仍然可能在.WhenAny()
確定完成與控制權最終返回給您之間的任何時間發生故障。 這是可以預期的,並且您的代碼應處理此問題(在下一輪等待中, .WhenAny()
將與錯誤的任務一起立即返回)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.