想象一下以下情况。有一个UI,并且必须调用长时间运行的操作而不阻塞Main线程。长时间运行的操作会自行调用其他方法,这些方法不会与UI线程交互。
在大多数情况下,从方法B和C调用的方法具有异步替代方法的同步替代方法。问题是:可以安全地使用它们而不是异步使用它们吗?可以说是DbContext.DbSet.Add而不是AddAsync
// UI
public async Task UiMethodAsync()
{
var result = await MethodAAsync();
}
// some component
public async Task<bool> MethodAAsync()
{
return await MethodBAsync().ConfigureAwait(false);
}
public async Task<bool> MethodBAsync()
{
return await MethodCAsync().ConfigureAwait(false);
}
public async Task<bool> MethodCAsync()
{
return await DbContext.Set<TEntit>.AnyAsync().ConfigureAwait(false);
}
我的问题是:是否有必要使所有方法异步以防止UI线程阻塞,还是像这样使方法B和C同步进行就足够了?
// UI
public async Task UiMethodAsync()
{
var result = await MethodAAsync();
}
// some component
public Task<bool> MethodAAsync()
{
return Task.FromResult(MethodB());
}
public bool MethodB()
{
return MethodC();
}
public bool MethodC()
{
return DbContext.Set<TEntit>.Any();
}
当然,这取决于MethodB和C在做什么,无论它们是否与ui线程交互。但是,假设他们没有,只需要计算事物并返回结果即可。
是否也需要使它们异步?我觉得不是。我认为这可以避免不必要的管理任务和线程的开销。
答案 0 :(得分:2)
返回Task
的方法将创建不会阻塞调用线程的期望。至少在相当长的时间内没有。但这不是由异步等待机制强制执行的。编写打破这种期望的方法是可能的,而且实际上非常容易。例如:
public Task DoStuffTheWrongWayAsync()
{
Thread.Sleep(1000); // Simulate a heavy computation, or a blocking call
return Task.CompletedTask;
}
任何调用此方法的线程将被阻塞一秒钟,然后将被交给完成的任务。我不知道此反模式是否具有确定的名称。请记住假异步,尽管这也可以用于the case,即呼叫者未被阻止,但另一个不良线程被阻止。最重要的是,行为良好的异步方法应立即返回Task
,使调用线程可以自由执行其他工作(例如,如果是UI线程,则响应UI事件)。
答案 1 :(得分:1)
仅添加async关键字还不足以使您的代码不被阻塞。
除非所有调用一直异步,否则返回Task的函数仍然会阻塞。
很难解释,但是要突出一点代码:
public async Task<bool> MethodA()
{
return Task.FromResult(MethodB());
}
等同于
public async Task<bool> MethodA()
{
bool b = MethodB();
return Task.FromResult(b);
}
现在很明显,第一行代码正在阻塞,并且直到第二行才创建任务。
答案 2 :(得分:1)
通常来说,如果要使某些内容异步,则从最低级别开始-实际在进行I / O工作的API。在您的示例中,AnyAsync
将成为异步的 first 事物。然后让异步从那里开始增长(从MethodC
开始,然后到MethodB
,然后到MethodA
,最后到UiMethod
)。
以"all the way"异步结束是正常的。
是否也需要使它们异步?我想不会。
是的。如果希望它们是异步的,则它们需要是异步的。 Task.FromResult
用于同步实现;他们不会是异步的。
想象一下以下情况。有一个UI ...从方法B和C调用的方法具有异步替代品的同步替代品。问题是:可以安全地使用它们而不是异步使用它们吗?
在UI世界中可以避免的一件事是将对同步方法的调用包装在Task.Run
中。例如:
public async Task UiMethodAsync()
{
var result = await Task.Run(MethodA);
}
这是一种有用的技术,适用于您拥有客户端应用程序,不想阻塞UI,又不想花时间将所有代码转换为异步的情况。请注意,这不不适用于服务器端应用程序,例如ASP.NET。