创建异步包装器的方法

本文关键字:方法 包装 异步 创建 | 更新日期: 2023-09-27 18:28:57

如何更好地为同步方法创建异步包装

// 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>)的方法,我使用第一个版本。

但我想知道你的意见。

这些版本之间有一般的区别吗

创建异步包装器的方法

两种解决方案都不正确。最好的答案是而不是公开同步代码的异步方法。我在博客上更深入地探讨了"为什么"。

如果您的代码是异步的,则使用asyncawait。如果不是,那就不要。调用方应该决定如何调用代码(例如,使用Task.Run)。

您应该使用"类似"V2:

public Task LongOpertionWrapperAsyncV2()
{
    return Task.Run(LongOperation);
}

async Task Executor()
{
    await LongOpertionWrapperAsyncV2().ConfigureAwait(false);
}

与V1相比,这为您节省了一个上下文切换。只要你不需要"等待另一个操作",并且异步任务是方法中的最后一个操作,你就可以返回任务而不是等待它,并将等待留给调用者(调用者也可以或不能添加ConfigureAwait)。

只有当您想要像HPT建议的那样提供TaskCreationOptions.LongRunning时,才需要Task.Factory.StartNew

更新:
正如Stephen已经说过的:很少有情况下你应该做async over sync(但确实有)。所以,在实现这样的东西之前,先想想你在做什么。简化说:如果是CPU限制的工作,不要做,如果是某种"等待IO",也许可以做。
我们在这里有一个案例,我们开发了一个"几乎异步"的库,用于控制不同的硬件设备。在那里,整个"通用库"是异步的,但一些低级别的设备驱动程序和/或访问库不支持async,所以在最底层,我们会做一些事情,比如:

public Task<byte[]> ReadAsync(int length)
{
    return Task.Run(() => hwDevice.Read(length));
}

hwDevice.Read仍然会锁定线程,但不会锁定CPU,因此在我们等待IO的那段时间里,UI会做出响应(在"实时"中,它周围还有一些取消和错误处理逻辑)。