Страницы

Поиск по вопросам

суббота, 7 марта 2020 г.

Почему исключения в асинхронном коде считаются необработанными?

#c_sharp #исключения #async_await


Есть код скачивания файлов, на нижнем уровне есть простой цикл с ограничением, чтобы
сделать несколько попыток скачивания, выглядит упрощенно вот так:

private static bool Failed = false;

static void Main(string[] args)
{
  TaskScheduler.UnobservedTaskException += (o, a) => { throw a.Exception; };
  ChapterDownload().Wait();
}

public static async Task ChapterDownload()
{
  try
  {
    var pages = Enumerable.Range(0, 20);
    var pTasks = pages.Select(page =>
    {
      return PageDownload()
      .ContinueWith(t => Console.WriteLine("{0}{1}", t.Status, t.IsFaulted));
    });
    await Task.WhenAll(pTasks.ToArray());
  }
  catch (Exception ex)
  {
    //
  }
}

public static async Task PageDownload()
{
  if (Failed)
    throw new Exception("boom");

  try
  {
    // var file = await new WebClient().DownloadDataTaskAsync(new Uri(@"http://example.com"));
    var file = await DownloadFile(new Uri(@"http://example.com"));
    if (file != null)
      throw new Exception("Restart download, downloaded file is corrupted");
  }
  catch (Exception ex)
  {
    Failed = true;
    await PageDownload();
  }
}

public static async Task DownloadFile(Uri uri)
{
  byte[] result;
  WebResponse response;
  var request = WebRequest.Create(uri);

  try
  {
    response = await request.GetResponseAsync();
    using (var memory = new MemoryStream())
    {
      await response.GetResponseStream().CopyToAsync(memory);
      result = memory.ToArray();
    }
  }
  catch (System.Exception ex)
  {
    return null;
  }
  if (response.ContentLength == result.LongLength)
    return result;
  return null;
}


Проблема в чём - в текущем виде, происходит UnobservedTaskException, с текстовкой
boom, т.е. явно моё. При этом, стоит мне заменить скачивание с самопала на webclient
- UnobservedTaskException больше не возникает. В чём разница?
    


Ответы

Ответ 1



Чтобы исключения задачи (Task) считались обработанными и не вызывали событие TaskScheduler.UnobservedTaskException необходимо их перевыбросить, например t.Result, t.Wait() или t.GetAwaiter().GetResult(), или просмотреть, обратившись с свойству Exception: t.Exception. В Вашем коде проблема возникает потому, что вы ждёте завершение не самих исходных задач, а их продолжений. В то время как продолжения не делают ничего, чтобы обработать исключения исходных задач. В результате исключения, возникшие в исходных задачах, остаются необработанными.

Комментариев нет:

Отправить комментарий