合同异步和同步代码

时间:2016-12-30 18:37:18

标签: c# asynchronous interface

有很多问题要问是否混合使用异步和同步代码。

大多数答案都说,为异步方法公开同步包装器,并为同步方法公开异步包装器是个坏主意。

但是,没有一个答案解决了您必须混合异步和同步代码的具体方案,以及如何避免因此而产生的常见陷阱。

请参阅以下示例:

class Program
{
    static void Main(string[] args)
    {
        IContract signatory = new SyncSignatory();
        signatory.FullfillContractAsync().Wait();
        signatory = new AsyncSignatory();
        signatory.FullfillContractAsync().Wait();
    }
}
using System.Threading.Tasks;

interface IContract
{
    Task FullfillContractAsync();
}
using System.Threading.Tasks;

class AsyncSignatory : IContract
{
    public async Task FullfillContractAsync()
    {
        await Task.Delay(5000);
    }
}
using System.Threading;
using System.Threading.Tasks;

class SyncSignatory : IContract
{
    public Task FullfillContractAsync()
    {
        Thread.Sleep(5000);
        return Task.FromResult<object>(null);
    }
}

或者:

using System.Threading;
using System.Threading.Tasks;

class SyncSignatory : IContract
{
    public Task FullfillContractAsync()
    {
        return Task.Run(() => Thread.Sleep(5000));
    }
}

在此示例中,SyncSignatory和AsyncSignatory表示两个可互换的类,因为它们执行类似的功能,但是它们以不同的方式执行这些功能 - 同步和异步。

如何在避免常见陷阱场景的同时混合合同同步和异步代码?

希望syncSig运行异步的用户怎么样,而是运行同步?

如果用户可以通过运行异步来优化syncSig,但不会更长,因为他们认为它已经运行异步?

2 个答案:

答案 0 :(得分:3)

  

这是混合异步和同步的正确方法吗?

大多数情况下是的。如果你有一个通用接口,其中一些实现将能够提供同步实现,但其他实现只能是异步实现,返回Task<T>是有意义的。一个简单的return Task.FromResult可能是合适的。但也有例外,见下文。

  

希望syncSig运行异步的用户怎么样,而是运行同步?

接口的某些方面无法用代码表示。

如果IContract要求它在返回Task之前不会阻塞调用线程超过X毫秒(这个形式不是一个合理的硬性要求,但是你得到了基本的想法,并且它无论如何都会阻止线程,这违反了合同,用户应该将其作为一个错误报告给实施syncSig的人。

如果您的IContract要求它不会抛出同步异常,那么使用Task.FromException或等价物报告任何错误,并且您的syncSig无论如何都会抛出同步异常,那也是合同冲突。

除此之外,如果syncSig只是立即返回正确的结果,那么没有理由为什么这会打扰任何用户。

  

如果用户可以通过运行异步来优化syncSig,但不会更长,因为他们认为它已经运行异步?

如果同步运行的syncSig不会导致任何问题,那么无关紧要,它还不需要进行优化。

如果同步运行的syncSig确实会导致问题,那么基本的调试工具应该很快告诉开发人员syncSig导致问题并且应该进行调查。

答案 1 :(得分:1)

  

但是,没有一个答案解决了您必须混合异步和同步代码的具体方案,以及如何避免因此而产生的常见陷阱。

那是因为这是一个坏主意。任何混合代码都应该是完全临时的,仅在转换到异步API时才存在。

那就是说,我写了an entire article on the subject

  

如何在避免常见陷阱场景的同时混合合同同步和异步代码?

正如我在blog post on async interfaces中所描述的那样,async是一个实现细节。任务返回方法可以同步完成。但是,我建议避免阻止实施;如果由于某种原因他们无法避免,那么我会仔细记录它们的存在。

或者,您可以使用Task.Run作为实现;我不建议这样做(原因为detailed on my blog),但如果您 有阻止实施,则可以选择此选项。

  

希望syncSig运行异步的用户怎么样,而是运行同步?

同步阻止调用线程。这通常只是UI线程的一个问题 - 见下文。

  

如果用户可以通过运行异步来优化syncSig,但不会更长,因为他们认为它已经运行异步?

如果调用代码是ASP.NET,那么它应该直接调用它await。同步代码将同步执行,这在该平台上是可取的。

如果调用代码是UI应用程序,那么它需要知道存在同步实现,并且可以将其调用包含在Task.Run中。这可以避免阻塞UI线程,无论实现是同步还是异步。