更新时间:2023-01-15 21:35:14
1)
var myValue = Task.Run(async () => await MethodAsync()).Result;
异步方法的同步部分 MethodAsync
2)
var myValue = MethodAsync().Result;
异步方法的同步部分 MethodAsync
将在调用方的线程中运行。
The synchronous part of the asynchronous method MethodAsync
will run in the caller's thread.
现在您可能会问,异步方法的同步部分是什么?
同步部分是异步方法中第一个 await
之前的所有内容。
The synchronous part is everything before the first await
inside the asynchronous method.
更准确地说:同步部分是未完成的等待中的第一个 await
之前的所有内容。
More precisely: The synchronous part is everything before the first await
of a non-completed awaitable.
通常同步部分是微不足道的,但是当我们谈论一个未知的外部API时,我们不能100%确定。
Usually the synchronous part is minuscule, but when we talk about an unknown external API we cannot be 100% sure.
在调用者线程或线程中运行阻塞代码之间的区别-pool线程可能不是那么重要。在这两种情况下,调用者的线程都会在异步调用的整个过程中被阻止。第一种方法( Task.Run
)是否提供任何优势?通常添加 Task.Run
来解决死锁问题,当等待
和等待/结果$时很容易发生c $ c>是混合的。如果您出于某种原因在内部使用
await
,或者外部API在内部使用 await
而不使用内部API,则可能会发生此类问题 ConfigureAwait(false)
。在这种情况下,您会立即注意到它,并且可能会对其进行修复。因此,主动使用 Task.Run
的好处是省心。缺点是使用线程池线程来运行该方法的同步部分。在大多数情况下,这部分很小,以微秒为单位,因此如果您遵循简单的方法,就不会感到内。
The difference between running blocking code in the caller's thread or in a thread-pool thread is probably not that important. In both cases the caller's thread will be blocked for the whole duration of the async call. Does the first approach (Task.Run
) offers any advantage? Usually the Task.Run
is added to solve problems of deadlocks, that can easily occur when await
and Wait/Result
are mixed. In your case such problems could occur if you use await
internally for some reason, or the external API uses await
internally without ConfigureAwait(false)
. In that case you'll notice it immediately and you'll probably fix it. So the advantage of using Task.Run
proactively is peace of mind. The disadvantage is that a thread-pool thread is utilized for running the synchronous part of the method. In most cases this part is very small, measured in μsec, so you shouldn't feel guilty if you follow the easy path.
更新:这是第一种方法的示例,还演示了外部方法的同步和异步部分:
Update: Here is an example of the first approach, that also demonstrates the synchronous and the asynchronous part of the external method:
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)
等待的良好做法。
In this case the prophilactic use of Task.Run
is redundant because the external library follows the good practice of awaiting with ConfigureAwait(false)
.
这里是第二种方法的示例:
Here is an example of the second approach:
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
,即使外部库中只有一个未配置的*** await
也会导致死锁。直接,无需 Task.Run
。
This code deadlocks. Even a single non-configured top-level await
inside the external library will cause a deadlock, if you request the Result
directly, without Task.Run
.