使用 Task.Run 调用异步方法似乎是错误的?

Calling an async method using a Task.Run seems wrong?

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

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

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

I'm also not convinced this is really an async method because it will still wait, right?

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

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

I think it's only advantage (even re-written) is to free up the main worker thread.

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

I'm also not convinced this is really an async method because it will still wait, right?

正如 Yuval 解释的那样,事实并非如此。你不应该使用同步而不是异步。

Now as I understand it that first Task.Run() is pointless and inefficient?

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

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

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

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