Как вызвать асинхронный метод из синхронного метода?

#asynchronous

#асинхронный

Вопрос:

Я пытаюсь вызвать асинхронный метод из синхронного метода, как показано ниже:

 1.  public List<Words> Get(string code)
    {           
        Task<List<Words>> taskWords = GetWordsAsync(code);
        var result = taskWords.Resu<
        return resu<

    }
    private  async Task<List<Words>> GetWordsAsync(string code)
    {
        var result = await codeService.GetWordsByCodeAsync(code);
        return resu<
    }
  

Но это приводит к взаимоблокировке, await не получает результатов от метода — 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.Resu<
        return resu<

    }
    private  async Task<List<Words>> GetWordsAsync(string code)
    {
        var result = await codeService.GetWordsByCodeAsync(code);
        return resu<
    }
  

Но я не понял контекста, почему это вызвало взаимоблокировку для 1-го способа, а 2-й работал нормально.

Я хотел бы знать: в чем разница между двумя способами? Является ли второй правильным способом вызова асинхронного метода из синхронного метода? Будет ли использование второго метода также приводить к взаимоблокировке в какой-то момент времени, если результат будет большим? или это надежный (безопасный) метод для использования?

Кроме того, пожалуйста, предложите какие-либо рекомендации, чтобы лучше это сделать, поскольку мне нужно выполнить 5 асинхронных вызовов из синхронного метода — точно так же, как taskWords, у меня есть taskSentences и т. Д.,

Примечание: я не хочу менять все на асинхронное. Я хочу вызвать асинхронный метод из синхронного метода.

Ответ №1:

Я не хочу менять все на асинхронное. Я хочу вызвать асинхронный метод из синхронного метода.

С технической точки зрения это не имеет смысла. Асинхронный означает, что он не блокирует вызывающий поток; синхронный означает, что это так. Итак, вы понимаете, что если вы блокируете асинхронный код, он перестает быть действительно асинхронным? Единственное преимущество асинхронного кода в том, что он не блокирует вызывающий поток, поэтому, если вы блокируете асинхронный код, вы в первую очередь лишаете его всех преимуществ асинхронности.

Единственный хороший ответ — пройти async весь путь. Однако есть некоторые редкие сценарии, в которых это невозможно.

В чем разница между двумя способами?

Первый выполняет асинхронный код напрямую, а затем блокирует вызывающий поток, ожидая его завершения. Вы сталкиваетесь с тупиковой ситуацией, потому что асинхронный код пытается возобновить работу в контексте вызова (что await происходит по умолчанию), и, предположительно, вы запускаете этот код в потоке пользовательского интерфейса или в ASP.NET Классический контекст запроса, который допускает только один поток в контексте одновременно. В контексте уже есть поток (блокирующий, ожидающий завершения задачи), поэтому async метод не может возобновиться и фактически завершиться.

Второй выполняет асинхронный код в потоке пула потоков, а затем блокирует вызывающий поток, ожидая его завершения. Здесь нет взаимоблокировки, потому что асинхронный код возобновляется в контексте его вызова, который является контекстом пула потоков, поэтому он просто продолжает выполняться в любом доступном потоке пула потоков.

Является ли второй правильным способом вызова асинхронного метода из синхронного метода?

Не существует «правильного» способа сделать это. Существует множество способов взлома, каждый из которых имеет разные недостатки, и ни один из них не работает в любой ситуации.

Будет ли использование второго метода также приводить к взаимоблокировке в какой-то момент времени, если результат будет большим? или это надежный (безопасный) метод для использования?

Это не приведет к взаимоблокировке. Однако то, что он делает, выполняется GetWordsAsync в потоке пула потоков. Пока GetWordsAsync он может быть запущен в потоке пула потоков, он будет работать.

Кроме того, пожалуйста, предложите какие-либо рекомендации, чтобы лучше это сделать

Я написал статью на эту тему. В вашем случае, если GetWordsAsync безопасно вызывать поток пула потоков, тогда «взлом пула блокирующих потоков» является приемлемым.

Обратите внимание, что это не лучшее решение; лучшее решение — полностью асинхронный. Но блокирование пула потоков является приемлемым взломом, если вы должны вызывать асинхронный код из синхронного кода (опять же, это «приемлемо», только если GetWordsAsync безопасно вызывать поток пула потоков).

Я бы рекомендовал использовать GetAwaiter().GetResult() вместо Result , чтобы избежать AggregateException ошибки обертки. Кроме того, аргументы явного типа не нужны, и в этом случае вы можете исключить / async await :

 var taskWords = Task.Run(() => GetWordsAsync(code));
return taskWords.GetAwaiter().GetResult();