异步和同步方法[重复]

Posted

技术标签:

【中文标题】异步和同步方法[重复]【英文标题】:Asynchronous and synchronous methods [duplicate] 【发布时间】:2018-02-24 15:56:10 【问题描述】:

我有一个异步方法:

public async Task<Foo> GetFooAsync();

我需要它的同步版本。像这样的:

public Foo GetFoo();

我真的不想完全重写GetFooAsync 的代码,我想做一些事情,比如

public Foo GetFoo() 

    return GetFooAsync().GetAwaiter().GetResult();

这是个好主意还是这种方法有什么不明显的问题?据我所知,如果我在同步上下文中使用GetFooAsync().Result,我可能会遇到死锁。但是GetFooAsync().GetAwaiter().GetResult()呢?

【问题讨论】:

return GetFooAsync().Result; ? 不要使用.Result 使用.GetAwaiter().GetResult(),因为后者处理聚合异常,前者不处理。 如果你想要一个同步版本然后单独添加一个 shync 重载 @Rahul 显然我不明白。你能用一个例子来解释你的想法吗? 我同意 Rahul 的观点,不要用同步调用来包装你的异步方法。咬紧牙关改写吧。 【参考方案1】:

混合同步/异步代码可能会导致死锁as described in this article(“一路异步”段落)。

问题是Task 延续运行的地方,这取决于当前的SynchronizationContext。如果同步计划安排在当前因调用Wait()/Result/GetResult() 而被阻塞的同一线程上,那么您将遇到麻烦

【讨论】:

【参考方案2】:

如果您想要一个同步版本,请单独添加该方法的同步重载,以获得 SOC(关注点分离)。 I.c,像下面这样添加一个你已经完成的重载

public Foo GetFoo() 

  ///code body

【讨论】:

一般来说,应该先写一个同步版本,然后修改为异步(可能带有标志),还是可以简单地将同步版本包装在一个工作线程中进行异步? @samusarin,不,如回答中所述,最好有两个不同版本的方法,即使在 .NET BCL 中也可以看到相同的版本【参考方案3】:

您应该将异步实现隐藏在同步运行的方法后面,例如通过使用

Task.Run(async () => await GetFooAsync());

要么单独实现同步版本,要么让消费者显式同步 GetFooAsync。 问题是,运行任务将消耗工作线程并且可用工作人员池是有限的。您可能会遇到阻塞代码。所以 API 使用者应该知道同步实现是异步处理的。

【讨论】:

如果你把名字改成public Foo SynchronisedGetFooAsync() 怎么办?这样消费者就知道它只是一个异步方法的包装器。

以上是关于异步和同步方法[重复]的主要内容,如果未能解决你的问题,请参考以下文章

Spring boot:使用异步方法作为同步方法

结合同步和异步方法调用,并根据异步的结果,我们需要循环同步方法调用

Async/Await 同步方法中的异步和异步方法中的异步

同步方法和异步方法的区别

同步调用,异步回调和 Future 模式

Java 多线程 同步和异步