c# - method - runsynchronously




Синхронно ждет асинхронной операции и почему Wait() заморозит программу здесь (3)

Вот что я сделал

private void myEvent_Handler(object sender, SomeEvent e)
{
  // I dont know how many times this event will fire
  Task t = new Task(() =>
  {
    if (something == true) 
    {
        DoSomething(e);  
    }
  });
  t.RunSynchronously();
}

отлично работает и не блокирует поток пользовательского интерфейса

Предисловие : Я ищу объяснение, а не просто решение. Я уже знаю решение.

Несмотря на то, что несколько дней изучали статьи MSDN о Asynchronous Pattern (TAP), основанные на задачах, async и ждут, я все еще немного запутался в некоторых более тонких деталях.

Я пишу журнал для Windows Store Apps, и я хочу поддерживать как асинхронные, так и синхронные протоколирования. Асинхронные методы следуют за TAP, синхронные должны скрывать все это, и выглядеть и работать как обычные методы.

Это основной метод асинхронного ведения журнала:

private async Task WriteToLogAsync(string text)
{
    StorageFolder folder = ApplicationData.Current.LocalFolder;
    StorageFile file = await folder.CreateFileAsync("log.log",
        CreationCollisionOption.OpenIfExists);
    await FileIO.AppendTextAsync(file, text,
        Windows.Storage.Streams.UnicodeEncoding.Utf8);
}

Теперь соответствующий синхронный метод ...

Версия 1 :

private void WriteToLog(string text)
{
    Task task = WriteToLogAsync(text);
    task.Wait();
}

Это выглядит правильно, но это не работает. Вся программа зависает навсегда.

Версия 2 :

Хм .. Может быть, задача не началась?

private void WriteToLog(string text)
{
    Task task = WriteToLogAsync(text);
    task.Start();
    task.Wait();
}

Это выдает InvalidOperationException: Start may not be called on a promise-style task.

Версия 3:

Hmm .. Task.RunSynchronously звучит многообещающе.

private void WriteToLog(string text)
{
    Task task = WriteToLogAsync(text);
    task.RunSynchronously();
}

Это вызывает InvalidOperationException: RunSynchronously may not be called on a task not bound to a delegate, such as the task returned from an asynchronous method.

Версия 4 (решение):

private void WriteToLog(string text)
{
    var task = Task.Run(async () => { await WriteToLogAsync(text); });
    task.Wait();
}

Это работает. Итак, 2 и 3 являются неправильными инструментами. Но 1? Что не так с 1 и какая разница с 4? Что заставляет замораживать 1? Есть ли проблемы с объектом задачи? Существует ли неочевидный тупик?

Пожалуйста, помогите мне понять.


Вызов async кода из синхронного кода может быть довольно сложным.

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

Поэтому, если это вызвано в контексте пользовательского интерфейса, когда await завершается, метод async пытается повторно ввести этот контекст для продолжения выполнения. К сожалению, код с использованием Wait (или Result ) блокирует поток в этом контексте, поэтому метод async не может быть завершен.

Руководящие принципы, чтобы избежать этого:

  1. Используйте ConfigureAwait(continueOnCapturedContext: false) как можно больше. Это позволяет вашим async методам продолжать выполнение без необходимости повторного ввода контекста.
  2. Используйте async полностью. Используйте await вместо Result или await .

Если ваш метод является естественно асинхронным, то вы (вероятно) не должны выставлять синхронную оболочку .


await внутри вашего асинхронного метода пытается вернуться к потоку пользовательского интерфейса.

Поскольку поток пользовательского интерфейса занят, ожидая завершения всей задачи, у вас есть тупик.

Перемещение асинхронного вызова в Task.Run() решает проблему.
Поскольку асинхронный вызов теперь запущен в потоке пула потоков, он не пытается возвращаться к потоку пользовательского интерфейса, и поэтому все работает.

В качестве альтернативы вы можете вызвать StartAsTask().ConfigureAwait(false) перед ожиданием внутренней операции, чтобы она возвращалась к пулу потоков, а не к потоку пользовательского интерфейса, полностью исключая тупик.







async-await