Task.Run(async () => await MethodAsync()) 之间有什么区别吗?结果和方法异步()。结果?



我需要实现一个不支持异步的第三方接口,特别是来自自动映射器的IValueResolver。

我想知道这两段代码有什么区别?使用第一个而不是第二个有什么好处吗?我将在 MethodAsync(( 上调用外部异步 API

两者都会锁定线程还是只锁定第二个线程?

1

var myValue = Task.Run(async () => await MethodAsync()).Result;

阿拉伯数字

var myValue = MethodAsync().Result;

1(

var myValue = Task.Run(async () => await MethodAsync()).Result;

异步方法的同步部分MethodAsync将在线程池线程中运行。

2(

var myValue = MethodAsync().Result;

异步方法MethodAsync的同步部分将在调用方的线程中运行。

现在您可能会问,异步方法的同步部分是什么?

同步部分是异步方法中第一个await之前的所有内容。

更准确地说:同步部分是未完成的等待的第一个await之前的所有内容。

通常同步部分是微不足道的,但是当我们谈论未知的外部API时,我们不能100%确定。

在调用方线程或线程池线程中运行阻塞代码之间的区别可能并不那么重要。在这两种情况下,调用方的线程都将在异步调用的整个持续时间内被阻止。第一种方法(Task.Run(有什么优势吗?通常添加Task.Run来解决死锁问题,当awaitWait/Result混合时很容易发生死锁问题。在您的情况下,如果您出于某种原因在内部使用await,或者外部 API 在内部使用await而不ConfigureAwait(false),则可能会出现此类问题。在这种情况下,您会立即注意到它,并且可能会修复它。因此,主动使用Task.Run的好处是安心。缺点是线程池线程用于运行方法的同步部分。在大多数情况下,这部分非常小,以微秒为单位,所以如果你遵循简单的路径,你不应该感到内疚。


更新:下面是第一种方法的示例,它还演示了外部方法的同步和异步部分:

private void Button1_Click(object sender, EventArgs e)
{
this.Text = YourMethod();
}
public static int YourMethod()
{
return Task.Run(async () => await ExternalMethodAsync()).Result;
}
public static async Task<string> ExternalMethodAsync()
{
Thread.Sleep(500); // Synchronous part
await Task.Delay(500).ConfigureAwait(false); // Asynchronous part
return $"Time: {DateTime.Now:HH:mm:ss.fff}";
}

在这种情况下,Task.Run的亲爱使用是多余的,因为外部库遵循等待ConfigureAwait(false)的良好做法。

下面是第二种方法的示例:

public static int YourMethod()
{
return ExternalMethodAsync().Result;
}
public static async Task<string> ExternalMethodAsync()
{
Thread.Sleep(500); // Synchronous part
await Task.Delay(500); // Asynchronous part
return $"Time: {DateTime.Now:HH:mm:ss.fff}";
}

此代码死锁。如果您直接请求Result而不Task.Run,即使外部库中的单个未配置的顶级await也会导致死锁。

根据该方法的实现方式,您很可能会在第二个实现上锁定线程。

如果你想安全,请使用第一个实现,因为你可以保证一个新的线程来处理异步函数,但是这将花费你一些可扩展性,因为你将使用 2 个线程而不是一个线程。

最新更新