我需要实现一个不支持异步的第三方接口,特别是来自automapper的IValueResolver。
我想知道这两段代码有什么区别?使用第一个而不是第二个有什么优势吗?我将在MethodAsync()上调用外部异步API
会同时锁定线程还是仅锁定第二个线程?
1
var myValue = Task.Run(async () => await MethodAsync()).Result;
2
var myValue = MethodAsync().Result;
答案 0 :(得分:1)
1)
var myValue = Task.Run(async () => await MethodAsync()).Result;
异步方法MethodAsync
的同步部分将在线程池线程中运行。
2)
var myValue = MethodAsync().Result;
异步方法MethodAsync
的同步部分将在调用者的线程中运行。
现在您可能会问,异步方法的同步部分是什么?
同步部分是异步方法中第一个await
之前的所有内容。
更准确地说:同步部分是未完成的第一个await
之前的所有内容。
通常,同步部分很小,但是当我们谈论未知的外部API时,我们不能百分百确定。
在调用者线程或线程池线程中运行阻塞代码之间的区别可能并不那么重要。在这两种情况下,调用者的线程都会在异步调用的整个过程中被阻止。第一种方法(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}";
}
此代码陷入僵局。如果您直接请求await
而不使用Result
,则即使是外部库中单个未配置的顶级Task.Run
也会导致死锁。
答案 1 :(得分:0)
根据方法的实现方式,很有可能将线程锁定在第二个实现上。
如果您想安全使用第一个实现,因为可以保证有一个新线程可以处理异步功能,但是这将使您具有一定的可伸缩性,因为您将使用2个线程而不是一个。