我有一個理論問題給你。如果我在另一個任務中等待任務的結果,會發生什麼情況?我想知道我的現有系統是否能在事後工作。當你等待一個失敗的任務時會發生什麼
任務啓動並執行一些操作。某些時候,任務可能需要另一個任務才能處理當前任務無法自行處理的數據。所以我用等待來確保當前的任務不會繼續,只要他沒有幫助者任務的結果。但是如果幫手失敗會發生什麼?目前的任務是否會被鎖定?
我能否以某種方式避免這種死鎖(不改變系統本身 - 任務內的任務)?
我有一個理論問題給你。如果我在另一個任務中等待任務的結果,會發生什麼情況?我想知道我的現有系統是否能在事後工作。當你等待一個失敗的任務時會發生什麼
任務啓動並執行一些操作。某些時候,任務可能需要另一個任務才能處理當前任務無法自行處理的數據。所以我用等待來確保當前的任務不會繼續,只要他沒有幫助者任務的結果。但是如果幫手失敗會發生什麼?目前的任務是否會被鎖定?
我能否以某種方式避免這種死鎖(不改變系統本身 - 任務內的任務)?
任務啓動並執行一些操作。某些時候,任務可能需要另一個任務才能處理當前任務無法自行處理的數據。所以我用等待來確保當前的任務不會繼續,只要他沒有幫助者任務的結果。但是如果幫手失敗會發生什麼?目前的任務是否會被鎖定?
async
和await
背後的核心思想是異步代碼的工作原理與同步代碼大致相同。
所以,如果你有同步代碼:
void HelperMethod()
{
throw new InvalidOperationException("test");
}
void DoStuff()
{
HelperMethod();
}
,那麼你會希望DoStuff
傳播從輔助方法InvalidOperationException
。同樣,與異步代碼會發生什麼:
async Task HelperMethodAsync()
{
throw new InvalidOperationException("test");
}
async Task DoStuffAsync()
{
await HelperMethodAsync();
}
也就是說,DoStuffAsync
也會傳播InvalidOperationException
。現在
,這是行不通的完全相同以同樣的方式,當然,因爲它必須是異步的,但總的想法是,你的所有控制流程,如try
/catch
,for
循環等,都「只是工作「異步代碼非常類似於同步代碼。
什麼實際上事情是,當HelperMethod
與InvalidOperationException
結束時,異常被捕獲並放置在返回Task
,並且任務完成。 DoStuffAsync
中的await
發現任務已完成時,它檢查其例外並重新提出第一個例外(在這種情況下,只有一個,即InvalidOperationException
)。它以一種保留異常調用堆棧的方式重新引發它。這又導致從DoStuffAsync
返回的Task
以相同的例外完成。
因此,蓋async
和await
下正在做一些工作,以確保你可以調用其他方法與await
和使用try
/catch
相同的方式,你會在同步代碼。但大多數時候你不必知道這一點。
感謝您的好解釋。與此相關的另一個問題是:當我調用「task.Result」(或者task.Wait())時,它確實被鎖定了,不是嗎?這是一個可能的死鎖源,還是安全? – SharpShade 2014-09-27 23:12:11
我相信你指的是[我在博客上描述的死鎖場景](http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html)。 – 2014-09-27 23:46:07
什麼巧合。我真的只是這種情況,甚至不是我最初擔心的問題。但是仍然非常有用,因爲這種情況現在恰好發生在那個位置上,我認爲它可以:我已經是異步的運行方法(方法本身不是異步的,但它運行在第二個線程上)創建另一個任務,並且必須等待它完成。同樣的問題: - /所以我真的必須重新設計我的整個系統,以便有可以等待這些任務的異步方法... – SharpShade 2014-09-28 00:14:31
這真的很容易測試。例如:
[TestMethod, ExpectedException(typeof(Exception))]
public async Task DoFaultedTaskThrowOnAwait()
{
var task = Task.Factory.StartNew(() => { throw new Exception("Error 42"); });
await task;
}
[TestMethod, ExpectedException(typeof(AggregateException))]
public void DoFaultedTaskThrowOnWait()
{
var task = Task.Factory.StartNew(() => { throw new Exception("Error 42"); });
task.Wait();
}
兩個測試都通過,注意到Wait
拋出AggregateException
,和await
拋出Exception
。
好吧,你是對的。但我還沒有使用過測試框架。我不知道這是那麼容易... – SharpShade 2014-09-27 21:56:35
如果我在另一個任務中等待任務的結果會發生什麼?
你只await
一個Task.Result
如果它是一個awaitable
可以(這意味着它有一個GetAwaiter
方法)。這種情況很少。我假設你的意思是await
在內部任務。
但是,如果助手失敗會發生什麼?目前的任務是否會被鎖定?
首先,我不知道你是什麼意思的「鎖定」。在內心的任務上,你的任務不會被鎖定。控制返回到調用方法,直到內部任務完成。如果該內部任務失敗並且未能正確處理該異常,則您的父任務也會出錯。你需要確保你處理異常優雅:
var task =
Task.Run(async() =>
{
try
{
await AsyncHelper.DoSomethingAsync();
}
catch (Exception e)
{
// Handle exception gracefully
}
});
如果await
父任務,你會發現內部異常來自未處理內任務propogating。
我可以以某種方式避免這種死鎖嗎?
我沒有看到你的代碼在這種特定情況下應該死鎖的原因。不知道爲什麼這讓你擔心。
如何編寫簡單的測試代碼並觀察結果? – 2014-09-27 19:34:41
你不能'等待'結果',你可以等待任務。如果失敗了,它會拋出異常 – 2014-09-27 19:34:56
當然我可以編寫測試代碼,但這要比在這裏詢問要花費更多的時間。而且這不應該太過努力回答:)否則我很抱歉。對不起,我的錯。當然,我的意思是等待任務。所以理論上,如果等待中的任務失敗,「await」會拋出異常? – SharpShade 2014-09-27 19:38:25