我最近一直在处理 async await(阅读所有可能的文章,包括 Stephen 和 Jon 的最后两章),但我已经得出结论,但我不知道它是否是100% 正确。 - 因此我的问题。
因为 async
只允许出现 await 这个词,所以我将把 async
放在一边。
AFAIU,等待就是延续。与其编写功能性(连续)代码,不如编写同步代码。 (我喜欢将其称为可回调代码)
因此,当编译器到达 await
时 - 它会将代码拆分为 2 个部分,并注册第二部分以在第一部分完成后执行(我不知道为什么这个词未使用 callback
- 这正是完成的操作)。 (同时工作 - 线程回来做其他事情)。
但是看看这段代码:
public async Task ProcessAsync()
{
Task<string> workTask = SimulateWork();
string st= await workTask;
//do something with st
}
public Task <string> SimulateWork()
{
return ...
}
当线程到达 await workTask;
时,它将方法拆分为 2 个部分。所以在 SimulateWork
完成后 - 方法的延续:又名://do something with st
- 被执行。
一切顺利
但是如果方法是:
public async Task ProcessAsync()
{
Task<string> workTask = SimulateWork();
await workTask; //i don't care about the result , and I don't have any further commands
}
这里 - 我不需要需要 continuation ,意思是 - 我不需要 await
来拆分方法,这意味着 - 我不需要 async/await 完全在这里!但我仍然会得到相同的结果/行为!
所以我可以这样做:
public void ProcessAsync()
{
SimulateWork();
}
问题:
- 我的诊断是否 100% 正确?
最佳答案
所以,你认为 await
正如问题的标题所暗示的那样,下面是多余的:
public async Task ProcessAsync()
{
Task<string> workTask = SimulateWork();
await workTask; //i don't care about the result , and I don't have any further
}
首先,我假设 “当 await
是最后一个” 你的意思是 “当 await
是唯一的 await
"。必须是这样,否则以下内容将无法编译:
public async Task ProcessAsync()
{
await Task.Delay(1000);
Task<string> workTask = SimulateWork();
return workTask;
}
现在,如果它是唯一的 await
,你确实可以这样优化它:
public Task ProcessAsync()
{
Task<string> workTask = SimulateWork();
return workTask;
}
但是,它会给您带来完全不同的异常传播行为,这可能会产生一些意想不到的副作用。问题是,现在可能会在调用者的堆栈上抛出异常,这取决于 SimulateWork
的方式。是内部实现的。我发布了 detailed explanation of this behavior . async
通常不会发生这种情况Task
/Task<>
方法,其中异常存储在返回的 Task
中目的。对于 async void
仍然可能发生方法,但这是一个 different story .
因此,如果您的调用者代码已准备好应对异常传播中的此类差异,那么跳过 async/await
可能是个好主意。无论您在哪里,只需返回一个 Task
相反。
另一件事是,如果您想发出一个即发即弃调用。通常,您仍然希望以某种方式跟踪已触发任务的状态,至少出于处理任务异常的原因。我无法想象我真的不在乎任务是否永远不会完成的情况,即使它所做的只是记录日志。
因此,对于“即发即弃”,我通常使用助手 async void
将待处理任务存储在某处以供以后观察的方法,例如:
readonly object _syncLock = new Object();
readonly HashSet<Task> _pendingTasks = new HashSet<Task>();
async void QueueTaskAsync(Task task)
{
// keep failed/cancelled tasks in the list
// they will be observed outside
lock (_syncLock)
_pendingTasks.Add(task);
try
{
await task;
}
catch
{
// is it not task's exception?
if (!task.IsCanceled && !task.IsFaulted)
throw; // re-throw
// swallow, but do not remove the faulted/cancelled task from _pendingTasks
// the error will be observed later, when we process _pendingTasks,
// e.g.: await Task.WhenAll(_pendingTasks.ToArray())
return;
}
// remove the successfully completed task from the list
lock (_syncLock)
_pendingTasks.Remove(task);
}
你可以这样调用它:
public Task ProcessAsync()
{
QueueTaskAsync(SimulateWork());
}
目标是立即在当前线程的同步上下文中抛出致命异常(例如,内存不足),而任务结果/错误处理被推迟到适当的时候。
关于使用即发即弃任务的有趣讨论here .
关于c# - 等待最后一次时不必要的异步/等待?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23382851/