Pull to refresh

Comments 15

Скажите, пожалуйста, в каких реальных ситуациях можно столкнуться с описанными проблемами?
Вызвать метод, возвращающий Task, не сделав на него await, например. Забыть элементарно.
Вам не кажется, что в этом случае можно получить ошибки пострашнее неотловленного Exception? Например, негарантированное выполнение метода?
Вообще говоря нет. Сейчас практически все методы возвращают так называемые «горячие» таски (то есть, не надо вызывать Start, как иногда было во времена чистого TPL), так что выполнение метода начнётся. А вот исключение мы рискуем не поймать вообще.
Мне казалось, что Start не гарантирует, что выполнение метода начнется прямо сейчас, только то, что он будет положен в очередь шедулера, разве нет?

(ну и начало выполнения метода тоже не гарантирует его завершения)
Всё, что попадает в очередь задач, рано или поздно будет выполнено, если
1) не упало/завершилось приложение
2) все потоки из пула не заблокированы синхронной операцией ожидания
… а приложение может завершиться по куче причин. Самый банальный (хотя и нереалистичный) сценарий — это забыть сделать await на таске в теле консольного приложения, после чего оно радостно закончится.
Например у вас есть метод для сохранения информации о пользователе. По результатам вы пушите событие об этом. И кто то из подписчиков запускает задачу и не дожидается её завершения, возможно считая, что неважно как отработает задача и не важно выполнится ли она вообще. Например это может быть запись в БД какой то трэкинговой информации.
Выглядит как хрупкий дизайн. Возможно, проблему надо на уровне дизайна же и решать?
Я знаю, что такое fire and forget, я просто считаю, что f-n-f должен быть гарантированно безопасным для выполняющей его системы (т.е. как раз 4.5 в этом вопросе несколько более логичен).
4.5, увы, как раз менее логичен, потому что async void (который суть чистый fire and forget — он не дает вызывающему опции подождать в принципе) все равно валит процесс. А вот async Task — теперь не валит. При этом в 4.0 поведение было одинаковое.

Имхо все же в fire and forget, последний должен быть явным, а не по умолчанию.

Но сам по себе дизайн с f'n'f вполне нормален.
Дизайн, включающий f-n-f — нормален, вопрос реализации f-n-f. Я просто всегда стараюсь следить, чтобы ошибки в «некритичных» ветвях выполнения (которые -and-forget) не могли всплыть до основного потока выполнения.
Мы с активным использованием тасков и await огребли массу проблем с неотловленными исключениями как раз из-за того, что все разработчики сидят на VS 2013 (и, соответственно, .NET 4.5), но продукт собирается в т.ч. и для VS 2010, а там может быть и .NET 4.0. Причем чаще это были даже не косяки, а просто попытка by design запустить таск в фоне и не ждать его.

После продолжительного хождения по граблям, кстати, решили, что мы не будем поддерживать 4.0 :) тем не менее, в процессе появилась пачка вспомогательных методов для того, чтобы явно управлять поведением тасков в ситуациях без await. Может, кому еще пригодится.
Sign up to leave a comment.

Articles