В настоящее время я работаю над в значительной степени асинхронным приложением, которое использует TAP повсюду. У каждого класса, который имеет методы для нереста Task
, также есть введенный в него TaskScheduler
. Это позволяет нам выполнять явное планирование задач, которые, как я понимаю, не так, как Microsoft идет с Async CTP.Async CTP - Рекомендуемый подход к планированию задач
Единственная проблема с новым подходом (неявное планирование) заключается в том, что наша предыдущая философия всегда была «мы знаем, что продолжение всегда будет определять их планировщик задач, поэтому нам не нужно беспокоиться о том, какой контекст мы завершаем задача на ".
Отказ от этого немного беспокоит нас только потому, что он работал очень хорошо с точки зрения избежания тонких ошибок потоковой передачи, потому что для каждого фрагмента кода мы видим, что кодер вспомнил, какой поток он включен. Если они пропустили задание планировщика задач, это ошибка.
Вопрос 1: Может ли кто-нибудь успокоить меня, что неявный подход - это хорошая идея? Я вижу так много проблем, которые возникают в ConfigureAwait (false) и явное планирование в устаревшем/стороннем коде. Как я могу, например, убедиться, что мой код «ожидание» всегда работает в потоке пользовательского интерфейса?
Вопрос 2: Таким образом, предполагая, что мы убираем все TaskScheduler
DI из нашего кода и начать использовать неявное планирование, как мы затем установить планировщик задач по умолчанию? Как насчет изменения планировщика на полпути через метод, прежде чем ждать дорогого метода, а затем снова установить его обратно?
(постскриптум я уже читал http://msmvps.com/blogs/jon_skeet/archive/2010/11/02/configuring-waiting.aspx)
Отличный ответ Стивену, но для уточнения: если ConfigureAwait (false) были использованы внутри методом async «A», то какой контекст я ожидал бы, если бы я ждал метод «A»? Путь потока, или он возобновит исходный контекст, прежде чем делать ожидаемый вызов метода «А»? –
Ответ Стивена достаточно прочен. Обратите внимание, что если вы устарели на старой модели, вы всегда можете создать настраиваемую оболочку (как и для ConfigureAwait()) и использовать ее в качестве метода расширения для задачи/задачи. Например, если ваш метод расширения был вызван ResumeOn (TaskScheduler ts), тогда код может выглядеть так: Ожидать Foo (...). ResumeOn (ts); И тогда у вас есть все те же семантики планирования, что и ваш собственный код, но со всеми улучшенными качествами потока/исполнения, которые «ожидают». –
@Lawrence: Каждый «слой» методов async передает свой контекст вниз, но не вверх. Поэтому, если 'A' вызывает' ConfigureAwait (false) ', то он завершит работу в пуле потоков. Затем, когда 'B' вызывает' await A() ', тогда' B' возобновится в * своем собственном * исходном контексте после 'await'. Тот факт, что 'A' заканчивается в пуле потоков, не влияет на остаток' B'. –