我最近在WinForm应用程序中遇到了这段代码,但我无法确定是否有任何理由async在Task.Run等待中的内部运行代码。
public async Task SaveStuff()
{
await Task.Run(() => SaveStuffAsync().ConfigureAwait(false));
await Task.Run(() => SendToExternalApiAsync().ConfigureAwait(false));
}
private async Task SaveStuffAsync()
{
await DbContext.SaveChangesAsync().ConfigureAwait(false);
}
private async Task SendToExternalApiAsync()
{
// some async code that is awaited with ConfigureAwait(false);
}
Run Code Online (Sandbox Code Playgroud)
如果没有Task.Run,这段代码会不会做完全相同的事情?
public async Task SaveStuff()
{
await SaveStuffAsync().ConfigureAwait(false);
await SendToExternalApiAsync().ConfigureAwait(false);
}
Run Code Online (Sandbox Code Playgroud)
如果没有Task.Run,这段代码会不会做完全相同的事情?
如果async方法中的代码实际上是异步的,则不会有任何不同。该操作Task将在线程池上执行,因此可能需要更多资源才能执行。从调用线程的角度来看,您不会注意到差异。
但是,如果async方法中的代码是(不是)同步的,则您会发现有所不同。请考虑以下方法:
private async Task DoWorkNotReallyAsync()
{
for (int i = 0; i < aVeryLargeNumber; i++)
{
DoSynchronousComputation();
}
}
Run Code Online (Sandbox Code Playgroud)
上面的方法具有异步签名,但实际上将同步运行,因此在执行线程时将其阻塞。将调用包装在中Task.Run将把执行调度到线程池。因此,Task.Run如果要确保对async方法的调用不会阻塞当前线程,则将任务包装在其中可能会很有用。
您的示例中调用的方法看上去确实是异步的,因此我看不出将这些任务包装在中的理由Task.Run。
方法返回 a 的事实Task并不意味着它立即返回。例如,在 I/O 操作之前,它可能需要进行一些耗时/CPU 消耗的设置。
因此,在客户端 UI 上,通常会看到 UI 外部的所有内容都在内部调用Task.Run。
话虽这么说,但情况并非如此:
public async Task SaveStuff()
{
await Task.Run(() => SaveStuffAsync().ConfigureAwait(false));
await Task.Run(() => SendToExternalApiAsync().ConfigureAwait(false));
}
Run Code Online (Sandbox Code Playgroud)
这会导致 UI 线程中的一次额外执行仅用于调度线程池上的工作。
这会更容易被接受:
public async Task SaveStuff()
{
await Task.Run(
async () =>
{
await SaveStuffAsync();
await SendToExternalApiAsync();
});
}
Run Code Online (Sandbox Code Playgroud)
无需调用,ConfigureAwait(false)因为它保证没有SynchronizationContext.
此代码段与上一个代码段之间的区别在于调用该代码的位置和方式,
| 归档时间: |
|
| 查看次数: |
128 次 |
| 最近记录: |