使用Task.Run调用异步方法似乎错了?

时间:2015-09-16 10:48:46

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

我最近遇到了这个由我们为我们工作的承包商编写的代码。它要么是非常聪明或愚蠢(我认为后者,但我想要第二个意见)。我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
}

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

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

2 个答案:

答案 0 :(得分:8)

  

我也不相信这实际上是一种异步方法,因为它仍会等待,对吧?

正如Yuval解释的那样。您不应该使用异步同步。

  

据我所知,首先Task.Run()是无意义且效率低下的?

实际上,以这种方式使用Task.Run是有价值的。

由于您正在阻止异步方法(您不应该这样做),因此您可能会陷入僵局。这种情况发生在UI应用程序和asp.net中,您有SynchronizationContext

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

因此,阻止很糟糕,但如果您最终使用Task.Run进行阻止会更安全。

答案 1 :(得分:7)

  

我也不相信这实际上是一种异步方法,因为它仍会等待,对吧?

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

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

  

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

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