我正在尝试从如下所示的同步方法中调用异步方法:
1. public List<Words> Get(string code)
{
Task<List<Words>> taskWords = GetWordsAsync(code);
var result = taskWords.Result;
return result;
}
private async Task<List<Words>> GetWordsAsync(string code)
{
var result = await codeService.GetWordsByCodeAsync(code);
return result;
}
但这会导致死锁,等待无法从方法中获取结果-GetWordsByCodeAsync 我做了一些研究,发现如果我们从同步方法中调用异步方法,则应该使用Task.Run
当我更改如下代码时,它起作用了:
2. public List<Words> Get(string code)
{
Task<List<Words>> taskWords = Task.Run<List<Words>>(async () => await GetWordsAsync(code);
var result = taskWords.Result;
return result;
}
private async Task<List<Words>> GetWordsAsync(string code)
{
var result = await codeService.GetWordsByCodeAsync(code);
return result;
}
但是我没有得到上下文,为什么它导致第一种方式和第二种方式都陷入僵局。
我想知道: 两种方式有什么区别? 第二种是从同步方法调用异步方法的正确方法吗? 如果结果很大,使用第二种方法是否还会在某个时间点导致死锁?还是使用防故障(安全)方法?
此外,请提出任何最佳做法,以更好地做到这一点,因为我必须从同步方法中进行5次异步调用-就像taskWords,我具有taskSentences等一样,
注意:我不想将所有内容更改为异步。我想从同步方法中调用异步方法。
答案 0 :(得分:0)
我不想将所有内容更改为异步。我想从同步方法中调用异步方法。
从技术角度来看,这没有任何意义。异步意味着它不会阻塞调用线程。同步意味着可以。所以您了解,如果阻塞异步代码,那么它不再是真正的异步了吗?异步代码的唯一好处是它不会阻塞调用线程,因此,如果阻塞异步代码,则首先要消除异步的所有优点。
唯一的好答案是async
all the way。不过,在一些罕见的情况下,这是不可能的。
两种方式有什么区别?
第一个直接执行异步代码,然后阻塞调用线程,等待其完成。您是running into a deadlock,因为异步代码正在尝试在调用上下文上恢复(await
默认情况下会执行此操作),并且大概是在UI线程或ASP.NET中运行此代码。经典请求上下文,一次仅允许一个线程在上下文中。上下文中已经有一个线程(一个线程正在阻塞,正在等待任务完成),因此async
方法无法恢复并实际完成。
第二个在线程池线程上执行异步代码,然后阻塞调用线程,等待其完成。这里没有死锁,因为异步代码在其调用上下文(线程池上下文)上恢复,因此它将继续在任何可用线程池线程上执行。
第二种方法是从同步方法中调用异步方法的正确方法吗?
没有“正确”的方法来执行此操作。骇客种类繁多,每种都有不同的弊端,并且无在每种情况下都有效。
如果结果很大,使用第二种方法还会在某个时间点导致死锁吗?还是使用防故障(安全)方法?
它不会死锁。但是,它是在线程池线程上执行GetWordsAsync
。只要GetWordsAsync
可以在线程池线程上运行,那么它将起作用。
此外,请提出任何最佳做法,以更好地做到这一点
我写了article on the subject。对于您而言,如果GetWordsAsync
可以安全地调用线程池线程,那么“阻塞线程池黑客”是可以接受的。
请注意,这不是最佳解决方案;最好的解决方案是始终保持异步状态。但是,如果必须必须从同步代码中调用异步代码,则阻塞线程池黑客是可以接受的黑客(同样,只有GetWordsAsync
可以安全地调用线程池线程时,它才是“可接受的”)
我建议使用GetAwaiter().GetResult()
而不是Result
,以避免出现错误的AggregateException
包装器。另外,显式类型参数也是不必要的,在这种情况下,您可以elide async
/await
:
var taskWords = Task.Run(() => GetWordsAsync(code));
return taskWords.GetAwaiter().GetResult();