Почему синхронное ожидание не работает в отдельном методе? - PullRequest
0 голосов
/ 11 июля 2019

Я хочу сделать синхронный метод асинхронным.

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

Итак, у меня есть мой асинхронный метод

public static async Task AsyncMethod();

Если я назову это так:

public static void SyncMethod()
{
  Task task = Task.Run(async () => { await AsyncMethod(); });
  task.Wait();
}

все отлично работает.

Но если я хочу обобщить свой подход и поместить эту логику в отдельный метод, в библиотеку утилит (такая библиотека является отдельной от моего проекта UWP), метод никогда не вернется. Вот код с подходом, который не работает:

public static class Utilities
{
public static void Sync(this Task task)
{
    if (task == null)
        return;

    Task t = Task.Run(async () =>
    {
        try
        {
            await task;
        }
        catch (Exception e)
        {
            s_log.InfoFormat("Exception while running task {0} due to {1}", task.Id, e.Message);
        }
    });
    t.Wait();
}
}

И для вызова метода я просто делаю:

public static void SyncMethod()
{
  AsyncMethod().Sync();
}

Кто-нибудь может объяснить эту дихотомию, пожалуйста?

EDIT:

В связи с ранними комментариями к вопросам позвольте мне уточнить конечную цель этого вопроса.

Проблема не в том, как решить синхронизацию против асинхронного паттерна. У меня уже есть хорошее решение для этого, и я объясняю это в первой части вопроса.

Более того, есть еще один вопрос ( Вызов асинхронного метода из методов действия синхронизации: Task.Run или ConfigureAwaits (false) ), который объясняет плюсы и минусы решения и т. Д.

Вместо этого проблема заключается в понимании того, почему эти 2 строки кода

  Task task = Task.Run(async () => { await AsyncMethod(); });
  task.Wait();

работает "in-line", но если я помещу их в другой метод (в другую библиотеку), они больше не будут работать.

Пожалуйста, найдите MVCE здесь: https://github.com/cghersi/UWPExamples/tree/master/SyncAntiPattern

1 Ответ

3 голосов
/ 12 июля 2019

У меня уже есть хорошее решение для этого, и я объясняю его в первой части вопроса.

У вас есть один взлом .Как и любой хак, он не будет работать везде.

Незначительное улучшение: используйте GetAwaiter().GetResult() вместо Wait();это позволит избежать использования оболочки AggregateException в условиях сбоя.

Вместо этого проблема состоит в том, чтобы понять, почему эти две строки кода работают «in-line», но если я помещу их в другой метод (вдругая библиотека) они больше не работают.

Разница в том, где вызывается AsyncMethod.В коде, который работает (избегает взаимоблокировок), AsyncMethod вызывается из Task.Run, то есть из потока пула потоков.Таким образом, он не захватывает свой контекст и избегает тупика .В коде, который не работает, AsyncMethod вызывается из потока пользовательского интерфейса, а затем поток пула потоков просто (асинхронно) ожидает завершения задачи;AsyncMethod захватывает контекст пользовательского интерфейса, и вы снова зашли в тупик.

...