Есть ли разница между всегда отбрасыванием возвращаемой задачи и созданием асинхронного недействительного метода?

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

Для этой задачи я нашел старую статью Стивена Клири в MSDN на NotifyTaskCompletion<T>. Листинг кода «Рисунок 4» в разделе Лучший подход выглядит следующим образом:

public sealed class NotifyTaskCompletion<TResult> : INotifyPropertyChanged
{
    public NotifyTaskCompletion(Task<TResult> task)
    {
        Task = task;
        if (!task.IsCompleted) {
            var _ = WatchTaskAsync(task);
        }
    }

    private async Task WatchTaskAsync(Task task) {
        [...]
    }

    [...]
}

Теперь мне интересно: если у меня есть такой метод, как WatchTaskAsync, который я никогда не хочу ждать (не ожидание этой задачи, по сути, является целью всего этого класса), есть ли разница в создании метода async void в первую очередь?

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

Компилятор сгенерирует для вас задачу независимо от вашего выбора. Таким образом, пока вы пишете опасный код, выигрыша в производительности нет. Настоящая проблема в том, что оба варианта плохи — не следует отбрасывать Task, а хранить его где-то и контролировать. И, возможно, уловить потенциальные исключения и отреагировать на них. В конце концов вам следует подождать, по моему мнению, этот дизайн не очень хорош.

freakish 15.04.2024 11:56

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

LWChris 15.04.2024 12:02

@Fildor Да, я отредактировал фрагмент кода, чтобы сделать это более очевидным :)

LWChris 15.04.2024 12:04

Если вам нужна задача «выстрелил и забыл», может быть полезно четко показать, что это и есть предполагаемое поведение. И что вы не использовали «async void», потому что не знали лучшего.

JonasH 15.04.2024 13:02
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
4
97
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Отказ от Task называется «выстрелил и забыл».
Запуск операции async void называется пожар-и-крушение.

Разница появляется в случае ошибки. Исключение «уволили и забыли» Task никогда не возникнет. Оно навсегда останется вне поля зрения. Исключение операции async void будет повторно создано в захваченном SynchronizationContext , и все известные SynchronizationContext громко распространяют свои исключения, открывая окна сообщений и т. д. В случае отсутствия SynchronizationContext, как в консольном приложении, исключение генерируется повторно. в ThreadPool, что приводит к сбою процесса через несколько миллисекунд после ошибки¹.

Если у вас есть Task и вы хотите, чтобы он воспринимался как async void, проще всего установить await на ThreadPool:

ThreadPool.QueueUserWorkItem(async _ => await task);

Метод ThreadPool.QueueUserWorkItem не понимает асинхронные делегаты, поэтому на самом деле этот асинхронный делегат async void. А поскольку ThreadPool не имеет контекста синхронизации, который async void мог бы захватить, вы получите сбой.

¹ Which sounds bad, but it's arguably better than the alternative. It's better to get a crash with an error message, than to get a non-responsive application that hangs spontaneously without giving any feedback.

Присвоения исключения Task достаточно, чтобы считать его выполненным, даже если эта задача собирается мусором, даже не просматриваясь? Я знал, что исключение, созданное внутри async void, приводит к сбою вашего приложения, но я не знал, что этого не происходит в случае отброшенных async Task возвратов.

LWChris 16.04.2024 10:33

@LWChris .NET Framework 4.0 была единственной версией .NET, в которой сбой Task с ненаблюдаемым исключением приводил к сбою процесса. Подробности смотрите в документации события TaskScheduler.UnobservedTaskException. Но даже тогда крах не был мгновенным. Процесс аварийно завершился, когда неисправный Task был собран мусором, что является недетерминированным событием. Это было ужасное дизайнерское решение, и, к счастью, они изменили его в следующем выпуске .NET.

Theodor Zoulias 16.04.2024 10:40

Другие вопросы по теме

Как я могу имитировать фабрику асинхронного общего кэша с помощью Moq?
Как дождаться рендеринга видимых изменений DOM?
Async/await полезен, когда независимые сетевые вызовы не должны блокировать друг друга
Почему я не вижу записи в консоли при использовании задачи async await?
Есть ли способ предотвратить вызов метода в асинхронном или неоднопоточном контексте во время компиляции?
Почему моя функция запроса React Query вызывается, хотя ее следует отключить?
Когда сопрограмма в asyncio фактически уступает выполнение?
Ищу рекомендации по использованию await при промежуточных асинхронных вызовах функций, которые могут вызывать ошибки
Консольное приложение C# не закрывается до тех пор, пока не будет нажата комбинация клавиш CTRL+C без цикла while
Перехватчики не поддерживаются внутри ошибки асинхронного компонента в проекте nextjs с использованием useQuery