如何从同步方法调用异步方法?

时间:2019-04-11 20:39:41

标签: asynchronous

我正在尝试从如下所示的同步方法中调用异步方法:

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等一样,

注意:我不想将所有内容更改为异步。我想从同步方法中调用异步方法。

1 个答案:

答案 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();