Подтвердить что ты не робот

Почему в "Задача" <T> монада не входит аннулирование?

Task<T> аккуратно держит "начатое, может быть закончено" вычисление, которое может быть составлено с другими задачами, сопоставлено с функциями и т.д. Напротив, F # async monad проводит вычисление "может начаться позже, возможно, теперь выполняется" вместе с CancellationToken. В С# вам обычно нужно прокручивать CancellationToken через каждую функцию, которая работает с Task. Почему команда С# решила обернуть вычисление в монаде Task, но не CancellationToken?

4b9b3361

Ответ 1

Более или менее они инкапсулировали неявное использование методов CancellationToken для С# async. Рассмотрим это:

var cts = new CancellationTokenSource();
cts.Cancel();
var token = cts.token;

var task1 = new Task(() => token.ThrowIfCancellationRequested());
task1.Start();
task1.Wait(); // task in Faulted state

var task2 = new Task(() => token.ThrowIfCancellationRequested(), token);
task2.Start();
task2.Wait(); // task in Cancelled state

var task3 = (new Func<Task>(async() => token.ThrowIfCancellationRequested()))();
task3.Wait(); // task in Cancelled state

Для неасинхронной лямбда мне пришлось явно связать token с task2, чтобы аннулирование распространялось правильно, предоставив его как аргумент new Task() (или Task.Run). Для async лямбда, используемого с task3, это происходит автоматически как часть кода инфраструктуры async/await.

Кроме того, любой token будет распространять отмену для метода async, тогда как для неасинхронного вычислительного new Task()/Task.Run lambda он должен быть тем же самым токеном, переданным конструктору задачи или Task.Run.

Конечно, нам все равно придется называть token.ThrowIfCancellationRequested() вручную, чтобы реализовать шаблон аннулирования сотрудничества. Я не могу ответить, почему команды С# и TPL решили реализовать его таким образом, но я думаю, они стремились не чрезмерно усложнять синтаксис async/await, но сохраняя его достаточно гибким.

Что касается F #, я не посмотрел на сгенерированный код IL асинхронного рабочего процесса, описанный в статье Tomas Petricek в блоге, которую вы связали. Тем не менее, насколько я понимаю, токен автоматически проверяется только в определенных местах рабочего процесса, соответствующих await в С# (по аналогии, мы могли бы звонить token.ThrowIfCancellationRequested() вручную после каждого await в С#). Это означает, что любая работа с процессором по-прежнему не будет отменена немедленно. В противном случае F # должно было бы испускать token.ThrowIfCancellationRequested() после каждой команды IL, что было бы существенным издержком.

Ответ 2

Сначала была выполнена задача содержать дополнительные функции в классе, но позже была изменена для агрегирования объекта с дополнительной поддержкой функций. Все во имя производительности.

http://blogs.msdn.com/b/pfxteam/archive/2011/11/10/10235962.aspx (см. "Задача реструктуризации" в статье). Статья Джозефа Хоага дает хорошее представление об оптимизации, сделанной в .NET 4.5. Я считаю, что было бы полезно прочитать, кто пытается сжать последние 10% производительности из async/wait.

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

Я не могу говорить за команду С# или BCL, но я предполагаю, что это оптимизация производительности, которая возможна только в компиляторе F # или производительности был несуществен для команды F #. SRP, baby!