1)
var myValue = Task.Run(async () => await MethodAsync()).Result;
异步方法MethodAsync
的同步部分将在线程池线程中运行。
2)
var myValue = MethodAsync().Result;
异步方法MethodAsync
的同步部分会在调用者的线程中运行。
现在你可能会问,异步方法的同步部分是什么?
同步部分是异步方法中第一个await
之前的所有内容。
更准确地说:同步部分是未完成的可等待对象的第一个 await
之前的所有内容。
通常同步部分是微不足道的,但当我们谈论未知的外部 API 时,我们不能 100% 确定。
在调用者线程或线程池线程中运行阻塞代码之间的区别可能并不那么重要。在这两种情况下,调用者的线程都将在异步调用的整个持续时间内被阻塞。第一种方法 (Task.Run
) 有什么优势吗?通常添加Task.Run
来解决problems of deadlocks,当await
和Wait/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
也会导致死锁。