Использование await Task.WhenAny () для игнорирования исключений - PullRequest
1 голос
/ 01 февраля 2020

Я недавно случайно обнаружил, что мне не нужно ждать Задачу, чтобы получить исключения, которые она генерирует для логики управления программой c.

До обнаружения этой функции мой код будет выглядеть следующим образом:

using (TcpClient client = new TcpClient())
{
    try
    {
        var ca = client.ConnectAsync("192.168.1.88", 9999);
        await await Task.WhenAny(ca, Task.Delay(5000));
        if (ca.IsCompleted)
            Console.WriteLine("Connected");
        else
            Console.WriteLine("Connection failed due to timeout");
    }
    catch
    {
        Console.WriteLine("Connection failed.");
    }
}

После обнаружения мой код выглядит следующим образом (который лично я считаю более читаемым)

using (TcpClient client = new TcpClient())
{
    var ca = client.ConnectAsync("192.168.1.88", 9999);
    await Task.WhenAny(ca, Task.Delay(5000));
    if (ca.IsFaulted || !ca.IsCompleted)
        Console.WriteLine("Connection failed due to timeout or exception");
    else
        Console.WriteLine("Connected");
}

Есть ли что-нибудь технически неправильно с намеренным оставлением ненаблюдаемых исключений, которые TaskScheduler игнорирует? Например, будут ли они выполнять резервное копирование и вызывать другие проблемы в среде async / await?

Поведение более подробно описано в этом вопросе .

1 Ответ

2 голосов
/ 03 февраля 2020

Например, будут ли они выполнять резервное копирование и вызывать другие проблемы в среде async / await?

Нет. Отказ от задачи не вызовет никаких подобных проблем. Фактически, большинство случаев использования Task.WhenAny приведет к тому, что задача будет отброшена.

Как указал Мар c в комментариях, этот подход действительно имеет некоторые дополнительные издержки: задача должно быть завершено, и исключение повторно вызывается для глобального события. Так что это неэффективно.

Есть ли что-то технически неправильно с намеренным оставлением ненаблюдаемых исключений, которые должны игнорироваться TaskScheduler?

Как указал GSerg в комментариях, это условие гонки в коде. Так как код на самом деле не использует результат «какая задача выполнена» из WhenAny, он может истечь, и затем соединение завершится, и код получит путь Connected. Так что я бы сказал, что это состояние гонки хорошее. Но если вы используете этот шаблон в другом месте, подумайте об условиях гонки в этом коде и о том, являются ли они приемлемыми.

В коде также есть некоторые другие недостатки, в дополнение к финализатору / необработанному исключению Обработчик события, упомянутый Маром c):

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

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

TL; DR: код будет работать, но имеет некоторые неэффективности и состояние гонки (в данном случае доброкачественное). Так что этот шаблон будет уместным только в некоторых прикладных ситуациях. Вот почему вы не видите такого рода паттернов.

...