[英]Waiting for any of 2 async methods to return a result
我想知道在處理異步編程時是否遵循最佳實踐。 我手頭的問題是:我同時與2個設備通話。 我可以使用SendMessageAsync(msg)
方法向他們發送消息。 兩種設備都同時收到此消息,但作為回報,它們中只有一個發送答復,而另一個根本不答復。
該方法還應接受CancellationToken
以便可以在超時或其他原因后取消整個事件。
因此,我編寫了此方法來讀取消息:
public async Task<Message> GetMessageAsync(CancellationToken token)
{
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(token, new CancellationToken(false)))
{
var device1 = Task.Run(async () => { return await GetDevice1Async(cts.Token); });
var device2 = Task.Run(async () => { return await GetDevice2Async(cts.Token); });
var response = await Task.WhenAny(device1, device2);
cts.Cancel(); //Only one device answers, so cancel the other other one
return response.Result;
}
}
我想知道我的解決方案是否遵循最佳實踐。 具體來說,我希望獲得很好的性能(我要與之交談的設備是USB設備,因此我希望能夠為它們提供快速服務)。 因此,我每次需要閱讀一條消息時都會對創建兩個任務並不滿意。
目前看來,我的解決方案有效,但是據報道,在某些計算機上,它運行緩慢。 但是,在我的機器上,它的運行速度非常快,所以我不知道是否是由於我的代碼中的問題或其他原因。
我做對了嗎? 有沒有辦法改善這個解決方案?
編輯:根據來自Jeroen Mostert和usr的建議,我將代碼更新如下:
public async Task<Message> GetMessageAsync(CancellationToken token)
{
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(token, new CancellationToken(false)))
{
var taskList = new List<Task<Message>> {
GetDevice1Async(cts.Token),
GetDevice2Async(cts.Token)
};
// wait for any operation to finish, then cancel the other one
var task = await Task.WhenAny(taskList);
cts.Cancel();
//ensure both operations are either finished or cancelled before returning
try {
await Task.WhenAll(taskList);
}
catch (OperationCanceledException)
{
//The exception is expected as is safe to ignore
}
return task.Result;
}
}
基本上,這很好。
但是,一個問題是您總是放棄一項任務。 它被要求取消自身,但是如果不這樣做,它將繼續運行。 這會積累資源使用情況,並可能導致您看到的緩慢。
在.NET IO中,很少會輕易取消。 例如,對於套接字,您只能關閉套接字才能取消IO。 與USB設備通話時,您需要確保取消確實有效。
一個不同的問題是, cts
可能在其令牌的最后使用之后被處置。 在您調用cts.Dispose()
,仍有一個正在運行的任務可能在令牌中注冊了自己。 我不確定是否可以保證正常工作。
您可以通過等待已取消的操作實際取消來解決此問題:
cts.Cancel();
await Task.WhenAll(tasks); //Maybe need to swallow exceptions.
return response.Result;
您可以將Task.Run(async
簡化為普通的方法調用。這會稍微改變語義。它會傳遞同步上下文,並同步執行async方法的某些部分。您可能想要或不希望這樣做。不是我的主觀評估是,此Task.Run
模式澄清了代碼,並使代碼更容易正確使用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.