【问题标题】: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 的观点,不要用同步调用来包装你的异步方法。咬紧牙关改写吧。

标签: c# async-await


【解决方案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() 怎么办?这样消费者就知道它只是一个异步方法的包装器。
    猜你喜欢
    • 1970-01-01
    • 2015-03-29
    • 2018-11-01
    • 2021-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-08
    • 2020-12-19
    相关资源
    最近更新 更多