C# 不必要的异步/等待何时是最后一次?
我最近一直在处理很多关于async Wait的问题(阅读所有可能的文章,包括Stephen和Jon的最后两章),但我已经得出结论,我不知道它是否100%正确。-这就是我的问题 由于C# 不必要的异步/等待何时是最后一次?,c#,.net,task-parallel-library,async-await,c#-5.0,C#,.net,Task Parallel Library,Async Await,C# 5.0,我最近一直在处理很多关于async Wait的问题(阅读所有可能的文章,包括Stephen和Jon的最后两章),但我已经得出结论,我不知道它是否100%正确。-这就是我的问题 由于async只允许单词wait出现,因此我将async放在一边 阿法尤,等待就是继续。编写同步代码,而不是编写函数(连续)代码。(我喜欢将其称为可回调代码) 因此,当编译器到达await-它将代码分成两部分,并在第一部分完成后注册第二部分执行(我不知道为什么没有使用回调这个词,这正是所做的)。(同时工作-线程正在返回执行
async
只允许单词wait出现,因此我将async
放在一边
阿法尤,等待就是继续。编写同步代码,而不是编写函数(连续)代码。(我喜欢将其称为可回调代码)
因此,当编译器到达await
-它将代码分成两部分,并在第一部分完成后注册第二部分执行(我不知道为什么没有使用回调
这个词,这正是所做的)。(同时工作-线程正在返回执行其他操作)
但是看看这个代码:
public async Task ProcessAsync()
{
Task<string> workTask = SimulateWork();
string st= await workTask;
//do something with st
}
public Task <string> SimulateWork()
{
return ...
}
这里-我不需要继续,意思是-我不需要await
来分割方法,这意味着-我根本不需要async/await而且我仍然会有相同的结果/行为
所以我可以这样做:
public void ProcessAsync()
{
SimulateWork();
}
问题:
- 我的诊断是否100%正确李>
- 你很接近了。这意味着你可以这样写:
public Task ProcessAsync() { // some sync code return SimulateWork(); }
这样,您就不必“支付”将方法标记为
的开销,但您仍然能够等待整个操作。因此,您认为下面的async
是多余的,正如问题的标题所示:wait
首先,我假设在“when公共异步任务ProcessAsync() { 任务=模拟工作(); wait workTask;//我不在乎结果,也没有进一步的问题 }
是最后一个”下,您的意思是,whenawait
是唯一的await
“。必须是这样的,否则以下内容将无法编译:await
现在,如果它是唯一的公共异步任务ProcessAsync() { 等待任务。延迟(1000); 任务=模拟工作(); 返回工作任务; }
,您确实可以这样优化它:等待
public Task ProcessAsync() { // some sync code return SimulateWork(); }
但是,它会给您带来完全不同的异常传播行为,这可能会产生一些意想不到的副作用。问题是,根据public Task ProcessAsync() { 任务=模拟工作(); 返回工作任务; }
的内部实现方式,现在可能会在调用方的堆栈上抛出异常。我贴了一封信。这通常不会发生在SimulateWork
async
/Task
方法中,其中异常存储在返回的Task
对象中。对于Task
方法,仍然可能发生这种情况,但这是一种错误 因此,如果您的调用方代码已准备好处理异常传播中的此类差异,最好尽可能跳过异步void
,而只返回async/await
任务 另一个问题是,如果你想发出一个火灾和遗忘电话。通常,您仍然希望以某种方式跟踪已启动任务的状态,至少出于处理任务异常的原因。我无法想象有一种情况,我真的不在乎任务是否从未完成,即使它所做的只是记录日志 因此,对于fire and forget,我通常使用helper
方法,将挂起的任务存储在某个地方供以后观察,例如:async void
你可以这样称呼它:readonly对象_syncLock=new object(); 只读哈希集_pendingTasks=新哈希集(); 异步无效队列任务异步(任务任务) { //将失败/取消的任务保留在列表中 //他们将在外面被观察 锁(同步锁) _添加(任务); 尝试 { 等待任务; } 抓住 { //这不是task的例外吗? 如果(!task.iscancelled&&!task.IsFaulted) 抛出;//重新抛出 //吞咽,但不要从_pendingTasks中删除出现故障/已取消的任务 //稍后,当我们处理挂起的任务时,将观察到错误, //例如:wait Task.WhenAll(_pendingTasks.ToArray()) 返回; } //从列表中删除成功完成的任务 锁(同步锁) _pendingTasks.Remove(任务); }
public Task ProcessAsync() { // some sync code return SimulateWork(); }
目标是在当前线程的同步上下文上立即抛出致命异常(例如,内存不足),而任务结果/错误处理将延迟到适当的时间public Task ProcessAsync() { QueueTaskAsync(SimulateWork()); }
有一个有趣的讨论是使用fire and forget任务。要100%纯重写,您仍然应该返回
,而不是任务
,并且应该返回无效
返回的模拟工作
-这样,任务
的调用方仍然可以ProcessAsync
(或等待
)在这个等待
要知道你什么时候完成了。你应该始终关心任务是如何完成的。@PauloMorgado我不同意这个说法。在我的示例中,只有当我需要知道任务上
发生了什么时,它才是相关的。在某些情况下,fire和forget是合法的。如果任务以错误结束,会发生什么?Jus我不喜欢在后台线程执行时处理线程内部的错误.如果延续如此重要,而A依赖于B,那么就像我说的,使用wait/continuewith@RoyiNamir如果您指的是ProcessAsync
事件处理程序,那么它们必须保持async void
以支持异步操作,这与async
不同,后者很容易被标记为ProcessAsync
,而没有真正的更改任何内容。是的,我知道,jsut提到他们将无法使用任务返回类型。需要了解的重要部分是,您的void retuasync