2016-12-30 117 views
1

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

大多数答案认为为异步方法公开同步包装并为同步方法公开异步包装是一个坏主意。

但是,没有一个答案能够解决您需要混合异步代码和同步代码的特定情况,以及如何避免因此而出现的常见缺陷。

请看下面的例子:

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,但不再更长,这是因为他们认为它已经运行了异步?

+0

@DanWilson修订。 –

+0

我真的不知道这个问题。消费者需要了解的唯一方法是其参数,名称和返回类型。这种方法在内部对内部调用者应该没有什么影响 – Jonesopolis

+0

我认为如果没有特定的用例,您的示例中目前太泛化了,我不认为您能够得到答案。谁会调用这些方法?你认为这是你的代码的热门路线吗? –

回答

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线程,无论实现是同步还是异步。

3

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

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

如果用户期望syncSig运行异步,但它运行同步?

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

如果它没有返回一个Task之前阻塞调用线程,超过X毫秒的时间你IContract的要求(在这个确切形式不是一个合理的硬性要求,但你的基本的想法),并且它阻止无论如何,这是一个违反合同的行为,用户应该将其报告为执行syncSig的人的错误。

如果您的IContract要求它不抛出同步异常,使用Task.FromException或同等报告报告任何错误,并且您的syncSig抛出异步异常,那也是合同违规。

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

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

如果syncSig同步运行不会导致任何问题,那没关系,它不需要进行优化。

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

+0

我连接的第一个场景如何避免比第二个场景更多的陷阱? (在SyncSignatory同步中运行同步代码并对其进行异步运行)。 –

+0

@FranciscoAguilera我在你的问题中没有看到任何链接,我无法从你的评论中猜出你的意思。 – hvd

+0

哦,它不是一个链接,它是在问题底部编辑更多代码。用异步代码包装它还是保持同步代码? –