【问题标题】:Calling an async method using a Task.Run seems wrong?使用 Task.Run 调用异步方法似乎是错误的?
【发布时间】:2018-08-17 17:41:48
【问题描述】:

我最近遇到了由我们为我们工作的承包商编写的这段代码。它要么非常聪明,要么很愚蠢(我认为是后者,但我想要第二个意见)。我对 async await 的速度不太快。

基本上它是这样工作的:

public bool Send(TemplatedMessageDto message)
{
    return Task.Run(() => SendAsync(message))
        .GetAwaiter()
        .GetResult();
}

public async Task<bool> SendAsync(TemplatedMessageDto message)
{
    //code doing stuff
    var results = await _externalresource.DothingsExternally();
    //code doing stuff
}

现在据我了解,第一个 Task.Run() 毫无意义且效率低下?真的应该是:

public bool Send(TemplatedMessageDto message)
{
    return SendAsync(message))
    .GetAwaiter()
    .GetResult();
}

public async Task<bool> SendAsync(TemplatedMessageDto message)
{
    //code doing stuff
    var results = await _externalresource.DothingsExternally();
    //code doing stuff
}

我也不相信这真的是一个异步方法,因为它仍然会等待,对吧?我认为它的唯一优点(甚至重写)是释放主工作线程。

有人可以确认第一个任务不应该在那里吗?

【问题讨论】:

  • 为什么使用GetAwaiter().GetResult() 而不是.Result?为什么等待而不是写public async Task&lt;bool&gt; Send ... await Task.Run();
  • 如果“其他东西”足够重,Task.Run 不是低效的 - async 不会使任何东西异步运行。 await 之前的所有内容都将在调用线程上运行。如果你需要它同时运行,你应该使用Task.Run,或者更好的是,将它提取到它自己的方法中,该方法将被Task.Run调用
  • @PanagiotisKanavos,嗯,是的,这确实让我想到了。我不认为他真的明白自己在做什么。
  • 你真的需要同步版本吗?

标签: c# multithreading asynchronous async-await task-parallel-library


【解决方案1】:

我也不相信这真的是一个异步方法,因为它仍然会等待,对吧?

正如 Yuval 解释的那样,它不是。你不应该使用同步而不是异步。

现在据我了解,第一个 Task.Run() 毫无意义且效率低下?

并非如此,以这种方式使用Task.Run 是有价值的。

由于您阻塞了异步方法(您不应该这样做),因此您有可能会死锁。这发生在 UI 应用程序和 asp.net 中,您有 SynchronizationContext

使用Task.Run 清除SynchronizationContext,因为它将工作卸载到ThreadPool 线程并消除了死锁的风险。

所以,阻塞是不好的,但如果你最终使用Task.Run 来做它会更安全。

【讨论】:

  • 您能否澄清一下阻塞异步方法(您不应该这样做) 位。我现在有点困惑。 _externalresource 是返回 Task&lt;List&lt;value&gt;&gt; 的第三方库,并且此方法(我的 Send() 方法)只需要确认该列表包含值。我打算将其更改为同步方法并使用.Result,但我认为您的说法是个坏主意?
  • @Liam 阻塞异步方法通常是个坏主意。它会损害性能并可能导致死锁。最好让整个流程异步。
  • @Liam 如果你必须有一个同步选项,最好一直使用同步调用(即var results = _externalresource.DothingsExternally()),但如果这不存在,那么异步同步是不可避免的。
  • 也许是图书馆很傻。我想我可以发布一个后续问题。感谢您的帮助。
  • 如果您有兴趣,我有posted a follow on question
【解决方案2】:

我也不相信这真的是一个异步方法,因为它仍然会等待,对吧?

您的承包商所做的是使用sync over async anti-pattern。他这样做可能是为了避免自己创建一个同步工作的附加方法。他不必要地调用Task.Run 并立即使用GetResult 阻止它。

如果发生这种情况,使用 GetAwaiter().GetResult() 将传播内部异常,而不是包装的 AggregateException

我认为它的唯一优点(甚至重写)是释放主工作线程。

您的版本和他的版本都会在执行时阻塞主线程,而他的版本也会使用线程池线程来阻塞。正如 Bar 所提到的,这可以帮助避免与同步上下文封送处理有关的问题的死锁。如果需要,我建议创建一个同步等效项。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-12
    • 1970-01-01
    • 1970-01-01
    • 2016-12-28
    相关资源
    最近更新 更多