如何为同步方法创建异步包装器更好?
// sync method
public void LongOperation()
{
//code...
}
// versions of wrapper
public async Task LongOpertionWrapperAsyncV1()
{
var task = Task.Factory.StartNew(LongOperation);
await task.ConfigureAwait(false);
}
public Task LongOpertionWrapperAsyncV2()
{
var task = Task.Factory.StartNew(LongOperation);
task.ConfigureAwait(false);
return task;
}
虽然两个版本的使用没有区别。
async Task Executor()
{
await LongOpertionWrapperAsyncV1();
await LongOpertionWrapperAsyncV2();
}
对于返回值的方法(Task <T>),我使用第一个版本。
但我想知道你的意见。
这些版本之间存在一般差异吗?
你应该使用“像V2这样的东西:
public Task LongOpertionWrapperAsyncV2()
{
return Task.Run(LongOperation);
}
和
async Task Executor()
{
await LongOpertionWrapperAsyncV2().ConfigureAwait(false);
}
与V1相比,这可以节省一个上下文切换。只要您不需要“等待另一个操作”并且async-Task是方法中的最后一个操作,您就可以返回任务而不是等待它并将await留给调用者(也可以添加或不添加) ConfigureAwait
)。
Task.Factory.StartNew
只有在你想提供像HPT建议的TaskCreationOptions.LongRunning
时才是必要的。
更新:
正如史蒂芬已经提到的那样:很少有你应该做async over sync
的情况(但有)。因此,在实现这样的事情之前,请考虑一下你正在做什么。简化说:如果是CPU限制工作,请不要这样做,如果它是某种“等待IO”可能会这样做。
我们在这里有一个案例,我们开发了一个“几乎完全异步”的库,它可以控制不同的硬件设备。整个“通用库”是异步的,但是一些低级设备驱动程序和/或访问库不支持async
,因此在最低级别我们执行以下操作:
public Task<byte[]> ReadAsync(int length)
{
return Task.Run(() => hwDevice.Read(length));
}
hwDevice.Read
仍然会锁定线程,但不会锁定CPU,因此UI在此期间响应我们正在等待IO(在“实时”中,它也是一些取消和错误处理逻辑)。
Neither solution is correct。最好的答案是不公开同步代码的异步方法。我更多地进入“为什么”on my blog。
如果您的代码是异步的,那么使用async
和await
。如果不是,那就不要了。它应该由调用者决定如何调用代码(例如,使用Task.Run
)。