2012-08-14 57 views
11

我有兩個異步方法Task<int> DoInt()Task<string> DoString(int value)。我有第三個異步方法Task<string> DoBoth(int value),其目標是異步執行DoInt(),將其輸出到DoString(int),結果爲DoBoth()的結果。繼續任務<T>任務<U>沒有封鎖結果

重要的制約因素是:

  1. 我可能沒有源代碼,DoInt()DoString(),所以我不能 修改它們。
  2. 我不希望在一個任務中的任何點
  3. 輸出(結果),以塊必須作爲輸入傳遞到下一個
  4. 的最終輸出(結果)是我想成爲的DoBoth()結果

關鍵是我已經有異步方法(即返回任務),我想從任務管道數據,沒有阻礙,返回最初的結果在最初的任務。我可以做以下所有除了在下面的代碼不會阻止:

代碼示例:

// Two asynchronous methods from another library, i.e. can't be changed 
Task<int> DoInt(); 
Task<string> DoString(int value); 

Task<string> DoBoth() 
{ 
    return DoInt().ContinueWith<string>(intTask => 
    { 
     // Don't want to block here on DoString().Result 
     return DoString(intTask.Result).Result; 
    }); 
} 

由於Task<string> DoString(int)已經是一個異步方法,我怎麼幹淨創建非阻塞延續呢?實際上,我想從現有的任務而不是從Func創建一個延續。

+1

DoInt是否將int作爲參數?是否?你有兩種方式在你的帖子... – 2012-08-14 15:46:53

+0

@ReedCopsey - 我修復了DoInt()的簽名。不管它是否有參數在這個例子中都不重要,但是我的例子中的不一致性令人困惑:)爲了清楚起見,也重命名了valueTask => intTask。 – MikeJansen 2012-08-14 21:04:49

回答

8

您可以編寫使用TaskExtensions.Unwrap爲整個產業鏈:

Task<string> DoBoth(int value) 
{ 
    Task<Task<string>> task = DoInt(value).ContinueWith(valueTask => 
    { 
     return DoString(valueTask.Result); 
    }); 

    return task.Unwrap(); 
} 

注意,這個假設DoInt被定義爲Task<int> DoInt(int value);,從你的描述不同,但遵循你的代碼示例。

如果DoInt不帶一個int參數(符合聲明),這可能成爲:

Task<string> DoBoth() 
{ 
    return DoInt().ContinueWith(t => DoString(t.Result)).Unwrap(); 
} 

作爲一個額外的 - 使用C#5和.NET 4.5(或異步目標包) ,你可以這樣寫:

async Task<string> DoBoth(int value) 
{ 
    int first = await DoInt(value); 
    return await DoString(first); 
} 
+0

'valueTask.Result'不會阻塞,而是DoString(valueTask.Result).Result'將被阻塞,直到由'DoString()'返回的任務完成。通過阻止,我並不是說原來的呼叫者被阻止;我只是說坐在那裏的線程可能會空閒等待另一個任務完成(在這種情況下,線程正在運行異步委託,但它仍然是系統資源透視圖中的一個線程)。 – MikeJansen 2012-08-14 15:49:51

+0

太棒了!裏德,如果你可以澄清;即使您採用'valueTask.Result',也不會阻止? – 2012-08-14 15:50:02

+1

@AndersGustafsson否 - 在抓取結果的位置,您處於延續狀態。 'valueTask'保證在那一點完成,所以'.Result'變成非阻塞的。 (但是,如果任務被取消或失敗,但它永遠不會阻止,它可能仍然會引發異常。) – 2012-08-14 15:55:21