2014-09-27 87 views
4

我有一個理論問題給你。如果我在另一個任務中等待任務的結果,會發生什麼情況?我想知道我的現有系統是否能在事後工作。當你等待一個失敗的任務時會發生什麼

任務啓動並執行一些操作。某些時候,任務可能需要另一個任務才能處理當前任務無法自行處理的數據。所以我用等待來確保當前的任務不會繼續,只要他沒有幫助者任務的結果。但是如果幫手失敗會發生什麼?目前的任務是否會被鎖定?

我能否以某種方式避免這種死鎖(不改變系統本身 - 任務內的任務)?

+3

如何編寫簡單的測試代碼並觀察結果? – 2014-09-27 19:34:41

+0

你不能'等待'結果',你可以等待任務。如果失敗了,它會拋出異常 – 2014-09-27 19:34:56

+0

當然我可以編寫測試代碼,但這要比在這裏詢問要花費更多的時間。而且這不應該太過努力回答:)否則我很抱歉。對不起,我的錯。當然,我的意思是等待任務。所以理論上,如果等待中的任務失敗,「await」會拋出異常? – SharpShade 2014-09-27 19:38:25

回答

4

任務啓動並執行一些操作。某些時候,任務可能需要另一個任務才能處理當前任務無法自行處理的數據。所以我用等待來確保當前的任務不會繼續,只要他沒有幫助者任務的結果。但是如果幫手失敗會發生什麼?目前的任務是否會被鎖定?

asyncawait背後的核心思想是異步代碼的工作原理與同步代碼大致相同。

所以,如果你有同步代碼:

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/catchfor循環等,都「只是工作「異步代碼非常類似於同步代碼。

什麼實際上事情是,當HelperMethodInvalidOperationException結束時,異常被捕獲並放置在返回Task,並且任務完成。 DoStuffAsync中的await發現任務已完成時,它檢查其例外並重新提出第一個例外(在這種情況下,只有一個,即InvalidOperationException)。它以一種保留異常調用堆棧的方式重新引發它。這又導致從DoStuffAsync返回的Task以相同的例外完成。

因此,蓋asyncawait下正在做一些工作,以確保你可以調用其他方法與await和使用try/catch相同的方式,你會在同步代碼。但大多數時候你不必知道這一點。

+0

感謝您的好解釋。與此相關的另一個問題是:當我調用「task.Result」(或者task.Wait())時,它確實被鎖定了,不是嗎?這是一個可能的死鎖源,還是安全? – SharpShade 2014-09-27 23:12:11

+1

我相信你指的是[我在博客上描述的死鎖場景](http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html)。 – 2014-09-27 23:46:07

+0

什麼巧合。我真的只是這種情況,甚至不是我最初擔心的問題。但是仍然非常有用,因爲這種情況現在恰好發生在那個位置上,我認爲它可以:我已經是異步的運行方法(方法本身不是異步的,但它運行在第二個線程上)創建另一個任務,並且必須等待它完成。同樣的問題: - /所以我真的必須重新設計我的整個系統,以便有可以等待這些任務的異步方法... – SharpShade 2014-09-28 00:14:31

1

這真的很容易測試。例如:

[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

+0

好吧,你是對的。但我還沒有使用過測試框架。我不知道這是那麼容易... – SharpShade 2014-09-27 21:56:35

2

如果我在另一個任務中等待任務的結果會發生什麼?

你只await一個Task.Result如果它是一個awaitable可以(這意味着它有一個GetAwaiter方法)。這種情況很少。我假設你的意思是await在內部任務。

但是,如果助手失敗會發生什麼?目前的任務是否會被鎖定?

首先,我不知道你是什麼意思的「鎖定」。在內心的任務上,你的任務不會被鎖定。控制返回到調用方法,直到內部任務完成。如果該內部任務失敗並且未能正確處理該異常,則您的父任務也會出錯。你需要確保你處理異常優雅:

var task = 
    Task.Run(async() => 
      { 
       try 
       { 
         await AsyncHelper.DoSomethingAsync(); 
       } 
       catch (Exception e) 
       { 
         // Handle exception gracefully 
       } 
      }); 

如果await父任務,你會發現內部異常來自未處理內任務propogating。

我可以以某種方式避免這種死鎖嗎?

我沒有看到你的代碼在這種特定情況下應該死鎖的原因。不知道爲什麼這讓你擔心。

相關問題