我一直在处理很多关于 异步等待 最近(读了所有可能的文章,包括Stephen和Jon的最后2章),但我已经得出结论,我不知道它是否100%正确。- 因此,我的问题。
因为 async
只允许存在等待这个词,我就不说了。async
撇开。
AFAIU, await是关于延续的.与其写功能型(延续型)代码,不如写同步型代码。(我喜欢把它称为 回调'能 编码)
所以当编译器到达 await
- 它将代码分成两部分,并将第二部分注册在第一部分完成后执行 ( 我不知道为什么这个词 callback
不使用 - 这正是我们要做的事情). (同时工作--线程又开始做其他事情了)。
但看这段代码。
public async Task ProcessAsync()
{
Task<string> workTask = SimulateWork();
string st= await workTask;
//do something with st
}
public Task <string> SimulateWork()
{
return ...
}
当线程到达 await workTask;
它把方法分成了两部分。SimulateWork
完成--继续执行方法:又名.A。//do something with st
- 被执行。
一切OK
但是 如果方法是 。
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
}
这里 - 我 不要 需要继续,意思是--我不需要再继续了 await
来拆分这个方法,这意味着--我不需要异步的 await 在这里! 我还是会有 同理 !
所以我可以这样做。
public void ProcessAsync()
{
SimulateWork();
}
问题:
所以,你认为 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
是内部实现的。我发了一个 详解. 这种情况通常不会发生在 async
Task
Task<>
方法,其中异常存储在返回的 Task
对象。它仍然可能发生在一个 async void
方法,但那是一个 异曲同工.
所以,如果你的调用者代码已经准备好接受这样的异常传播差异,那么最好跳过 async/await
无论你在哪里,只需返回一个 Task
而不是。
另一个问题是,如果你想发出一个 弃之不顾 召唤. 通常情况下,你还是希望以某种方式跟踪已发射任务的状态,至少是为了处理任务异常。我无法想象,如果任务从未完成,我真的不会在意,即使它所做的只是记录。
所以,对于fire-and-forget,我通常会使用一个帮助程序 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());
}
我们的目标是在当前线程的同步上下文中立即抛出致命的异常 (例如,内存不足),而任务的resulterror处理被推迟到适当的时候。
关于使用带有fire-and-forget的任务,有一个有趣的讨论。此处.
你很接近了。这意味着你可以这样写。
public Task ProcessAsync()
{
// some sync code
return SimulateWork();
}
这样你就不用为将方法标记为... async
但你仍然保持着等待整个操作的能力。