powered by simpleCommunicator - 2.0.27     © 2024 Programmizd 02
Map
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Вызов асинхронного метода из синхронного
5 сообщений из 5, страница 1 из 1
Вызов асинхронного метода из синхронного
    #40063903
WinterGraveyard
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Добрый день.
Такая ситуация:
1. Есть асинхронный метод, который, помимо await-вызовов, взаимодействует с UI (WPF, если это существенно).
2. Этот асинхронный метод, в зависимости от внешних обстоятельств, может выбрасывать ошибки, и такие ситуации нужно обрабатывать в т.ч. на уровне UI.
3. Модифицировать этот асинхронный метод (чтобы встроить туда обработку ошибок) нельзя (мопед не мой).
3. Этот асинхронный метод нужно вызвать из синхронного. При этом не нужно дожидаться окончания отработки этого метода, вполне устроит просто запустить его - но вот как быть с обработкой ошибок?
Попробовал вот так (схематично):
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
public ObservableCollection<string> Progress { get; } = new ObservableCollection<string>();
....
async Task AsyncTest()
{
  Console.WriteLine("AsyncTest: start");
  await Task.Delay(TimeSpan.FromSeconds(1));
  Progress.Add("one");
  await Task.Delay(TimeSpan.FromSeconds(1));
  Progress.Add("two");
  await Task.Delay(TimeSpan.FromSeconds(1));
  Progress.Add("three");
  Console.WriteLine("AsyncTest: before exception");
  new XmlDocument().LoadXml("");
  Progress.Add("four");
  Console.WriteLine("AsyncTest: end");
}


(Progress привязан в качестве ItemsSource к листбоксу - просто для имитации нескольких последовательных взаимодействий с UI).
Пытаюсь запустить:
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
void Test()
{
  Task.Factory
    .StartNew(
      async ()=>await AsyncTest(),
      CancellationToken.None,
      TaskCreationOptions.None,
      TaskScheduler.FromCurrentSynchronizationContext()
    )
    .ContinueWith(PrepareError, TaskContinuationOptions.OnlyOnFaulted);
}

void PrepareError(Task<Task> task)
{
  Console.WriteLine("Error occured");
}


Task.Factory.StartNew использовался для запуска потому, что он позволяет задать контекст синхронизации (а в Task.Run я такой возможности не нашёл). Требуемый метод запускается вполне нормально, первые 3 строки добавляются в листбокс, но PrepareError не вызывается - как выяснилось, по причине указания TaskContinuationOptions.OnlyOnFaulted. Если параметр с TaskContinuationOptions вообще убрать, то по консольному выводу видно, что PrepareError вызывается ещё до окончания отработки AsyncTest (и до возникновения ошибки). Как быть в данном случае?
...
Рейтинг: 0 / 0
Вызов асинхронного метода из синхронного
    #40063914
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WinterGraveyard
При этом не нужно дожидаться окончания отработки этого метода
ну так запусти его в отдельном потоке, при этом пусть поток ждет Result или Wait от этого метода (тогда он словит и исключение), но ты сам этот запущенный поток не жди, а только обрабатывай коллбэки.
...
Рейтинг: 0 / 0
Вызов асинхронного метода из синхронного
    #40063949
fkthat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А что мешает сделать свой метод асинхронным как "async void":

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
async void Test()
{
    try 
    {
        await AsyncTest();
    }
    catch(Exception e)
    {
        Console.Write(e);
        // тут можно рисовать в гуй
    }
}
...
Рейтинг: 0 / 0
Вызов асинхронного метода из синхронного
    #40064056
WinterGraveyard
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
fkthat,

Спасибо, и действительно - что-то я так завяз в разбирательстве с ContinueWith, что такой простой вариант упустил.
Однако хотелось бы всё же разобраться - просто для лучшего понимания - почему в моём вышеприведённом случае этот continuation task выполняется без ожидания выполнения асинхронного делегата? Та же внутри имеется await - получается, что он ничего не ждёт, а просто создаёт Task, запускает его, и возвращает выполнение? Несмотря на await?
...
Рейтинг: 0 / 0
Вызов асинхронного метода из синхронного
    #40064117
Сон Веры Павловны
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WinterGraveyard
Та же внутри имеется await - получается, что он ничего не ждёт, а просто создаёт Task, запускает его, и возвращает выполнение? Несмотря на await?

Да, именно так. Task.Run с асинхронным делегатом и await ждёт, Task.Start - нет: https://stackoverflow.com/a/31584851
Using an async delegate inside Task.Run means that you actually run a Task<Task>. This is hidden from you by the fact that Task.Run is async aware and unwraps the inner task for you, something that Task.Factory.StartNew didn't do
и этот факт виден по требуемой сигнатуре Action для ContinueWith - в случае для Task.Run это Action<Task> (если не передавать состояние) - то есть действительно Task.Run unwraps the inner task for you .
А для Task.Start это Action<Task<Task>>, и никакого unwrap нет.
В данном случае, поскольку Task.Run использовать не получается, то можно сделать так:
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
void Test()
{
  var t = Task.Factory
    .StartNew(
      async () => await AsyncTest(),
      CancellationToken.None,
      TaskCreationOptions.None,
      TaskScheduler.FromCurrentSynchronizationContext()
    );
  t.Wait();
  t.Result.ContinueWith(
    PrepareError,
    null,
    CancellationToken.None,
    TaskContinuationOptions.OnlyOnFaulted,
    TaskScheduler.FromCurrentSynchronizationContext()
  );
}

void PrepareError(Task task, object o)
{
  Exception e = task.Exception;
  Console.WriteLine("Shit happens: {0}", e);
  if (e is AggregateException ae)
    e = ae.InnerException;
  Progress.Add($"{e.GetType()}: {e.Message}");
}


Я не думаю, что очень ожидание выполнения микротаска на старт другого таска будет критичным для чего-либо.
Но лучше, конечно, предложенный выше способ (с заворачиванием вызова метода в свой async-метод с обработкой ошибок). Он как минимум нагляднее.
...
Рейтинг: 0 / 0
5 сообщений из 5, страница 1 из 1
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Вызов асинхронного метода из синхронного
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали тему (0):
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]