2016-07-26 24 views
-7

我正在實現函數的異步和正常版本。我已經實現了異步版本。對於正常版本,我可以複製粘貼並用其正常計數器部件替換異步函數調用的使用。或者我可以使用Result調用異步版本,如下所示。使用帶結果的異步版本實現函數

第一種方法:

public int SomeTask(int param) 
{ 
    //Something going on 
    return SomeOtherTask(); 
} 

public async Task<int> SomeTaskAsync(int param) 
{ 
    //Something going on (copy pasted) 
    return await SomeOtherTaskAsync();  
} 

第二種方法:

public int SomeTask(int param) 
{ 
    return SomeTaskAsync(param).Result; 
} 

public async Task<int> SomeTaskAsync(int param) 
{ 
    //some function calls with await 
} 

是否與第二種方法可能出現的問題?

+3

對你可能想讀 - [?我應該公開的同步方法異步包裝器(http://blogs.msdn.com/b/pfxteam/ archive/2012/03/24/10287244.aspx)和[我應該公開異步方法的同步包裝?](http://blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx )。兩篇文章的答案通常都不是。 –

回答

2

性能將受到影響。有多少是不可能告訴的 - 你是代碼的人,你可以爲你的確切用例做分析。

但是,製作只調用Result的方法的同步版本毫無意義 - 您的方法的用戶可以執行同樣的操作。問題是,無論如何,這可能是相當危險的,特別是當涉及同步上下文時。考慮示例代碼:

async void btnTest_Click(object sender, EventArgs e) 
{ 
    await DoSomethingAsync(); 
} 

async Task DoSomethingAsync() 
{ 
    await Task.Delay(1000); 
} 

這工作正常。現在,讓我們來試試你的「同步」的版本:

async void btnTest_Click(object sender, EventArgs e) 
{ 
    DoSomethingAsync().Result; 
} 

async Task DoSomethingAsync() 
{ 
    await Task.Delay(1000); 
} 

哎呀,你有一個僵局。 UI線程正在等待DoSomethingAsync完成,但DoSomethingAsync需要在UI線程上完成執行。如果您同步等待,則永遠不會假定async方法將運行。

此外,通過使用Result而不是await,您將失去很多異常處理功能。例如,異常堆棧跟蹤將全部搞亂,您需要處理由創建任務的方法和調用本身的方法拋出的異常 - 第一個將拋出異常直至第一個點實際上必須等待,並且所有延續的第二個。你永遠不知道哪個是哪個。

0

首先,如果你有一個自然異步操作,你應該公開一個異步API。正如達米恩指出的那樣,"Should I expose synchronous wrappers for my asynchronous methods?"的答案是「否」。

這些類型的包裝的一個問題是,沒有模式適用於所有情景!我在我的文章Brownfield Async上描述了各種異步同步攻擊。他們每個人都有缺點;正如Luaan指出的那樣,阻止黑客有可能發生死鎖。但是,如果你有這樣一個真正的理由(即你的庫歷史上有同步方法,並且你正在添加異步方法,但想保持同步方法的向後兼容性,至少對於一個版本或者兩個),那麼你可以使用我的文章中描述的「布爾標誌黑客」。

Stephen Toub前段時間向我展示了這個技巧。這個想法是(私有)實現函數採用參數bool sync,如果是true,那麼它返回的任務保證已經完成。這避免了經常堵塞僵局問題:文章

private async Task<int> SomeTaskAsync(int param, bool sync) 
{ 
    // Every `await` in your code needs to honor `sync` 
    if (sync) 
    return SomeOtherTask(); 
    else 
    return await SomeOtherTaskAsync(); 
} 

public int SomeTask(int param) 
{ 
    return SomeTaskAsync(param, sync: true).GetAwaiter().GetResult(); 
} 

public Task<int> SomeTaskAsync(int param) 
{ 
    return SomeTaskAsync(param, sync: false); 
}