Почему CancellationToken отделен от CancellationTokenSource? - программирование
Подтвердить что ты не робот

Почему CancellationToken отделен от CancellationTokenSource?

Я ищу обоснование того, почему .NET CancellationToken была введена в дополнение к классу CancellationTokenSource. Я понимаю, как использовать API, но хочу также понять, почему он разработан таким образом.

I.e., почему мы имеем:

var cts = new CancellationTokenSource();
SomeCancellableOperation(cts.Token);

...
public void SomeCancellableOperation(CancellationToken token) {
    ...
    token.ThrowIfCancellationRequested();
    ...
}

вместо прямого прохождения CancellationTokenSource вокруг:

var cts = new CancellationTokenSource();
SomeCancellableOperation(cts);

...
public void SomeCancellableOperation(CancellationTokenSource cts) {
    ...
    cts.ThrowIfCancellationRequested();
    ...
}

Это оптимизация производительности, основанная на том, что проверки состояния отмены происходят чаще, чем передача маркера вокруг?

Чтобы CancellationTokenSource мог отслеживать и обновлять CancellationTokens, и для каждого токена проверка отмены - это локальный доступ к полю?

Учитывая, что в обоих случаях достаточно volatile bool без блокировки, я все еще не понимаю, почему это было бы быстрее.

Спасибо!

4b9b3361

Ответ 1

Я принимал участие в разработке и реализации этих классов.

Краткий ответ - "разделение интересов". Совершенно верно, что существуют различные стратегии реализации, и что некоторые из них проще, по крайней мере, в отношении системы типов и начального обучения. Однако CTS и CT предназначены для использования во множестве сценариев (таких как глубокие стеки библиотек, параллельные вычисления, асинхронизация и т.д.) И, таким образом, были разработаны с учетом многих сложных вариантов использования. Это дизайн, предназначенный для поощрения успешных шаблонов и предотвращения анти-шаблонов без ущерба для производительности.

Если оставить дверь открытой для API-интерфейсов неправильного поведения, то полезность дизайна отмены может быть быстро подорвана.

CancellationTokenSource == "триггер отмены", плюс генерирует связанных слушателей

CancellationToken == "Отмена слушателя"

Ответ 2

У меня был тот же самый вопрос, и я хотел понять обоснование этого дизайна.

Принятый ответ получил правильное обоснование. Здесь подтверждение от команды, которая разработала эту функцию (выделение мое):

В основе структуры лежат два новых типа: A CancellationToken - это структуру, которая представляет собой "потенциальный запрос на отмену". Эта struct передается в вызовы методов как параметр, и метод может опросить его или зарегистрировать обратный вызов, который будет запущен, когда аннулирование просил. A CancellationTokenSource - это класс, который предоставляет механизм для инициирования запроса аннулирования, и он имеет токен свойство для получения ассоциированного токена. Было бы естественно объединить эти два класса в один, , но эта конструкция позволяет двум ключевые операции (инициирование запроса аннулирования против наблюдения и отвечая на отмену), чтобы быть четко разделенными. В частности, методы, которые принимают только CancellationToken, могут наблюдать отмену запрос, но не может инициировать его.

Ссылка: .NET 4 Cancellation Framework

По моему мнению, факт, что CancellationToken может только наблюдать за состоянием и не изменять его, чрезвычайно критичен. Вы можете раздавать маркер как конфету и никогда не беспокоиться о том, что кто-то другой, кроме вас, отменит его. Он защищает вас от враждебного кода третьей стороны. Да, шансы тонкие, но мне лично нравится эта гарантия.

Я также чувствую, что он делает API более чистым и позволяет избежать случайной ошибки и способствует лучшему дизайну компонентов.

Посмотрите на открытый API для обоих этих классов.

CancellationToken API

CancellationTokenSource API

Если бы вы их объединили, при написании LongRunningFunction я увижу такие методы, как те множественные перегрузки "Отмена", которые я не должен использовать. Лично мне тоже не нравится использовать метод Dispose.

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

Позвольте мне задать вам вопрос, вы задались вопросом, в чем заключается цель токена. Реестр? Для меня это не имело смысла. И затем я прочитал Отмена в управляемых потоках, и все стало совершенно прозрачным.

Я считаю, что дизайн Framework Cancellation Framework в TPL абсолютно высок.

Ответ 3

Они отделены не по техническим причинам, а по смысловым. Если вы посмотрите на реализацию CancellationToken под ILSpy, вы найдете его просто оберткой вокруг CancellationTokenSource (и, следовательно, не отличаться от производительности, чем обход ссылки).

Они обеспечивают такое разделение функциональности, чтобы сделать вещи более предсказуемыми: когда вы передаете метод CancellationToken, вы знаете, что вы все еще единственный, кто может отменить его. Разумеется, метод все равно мог бы выбросить TaskCancelledException, но сам CancellationToken и любые другие методы, ссылающиеся на один и тот же токен, останутся в безопасности.

Ответ 4

The CancellationToken - это структура, в которой может существовать множество копий из-за передачи ее методам.

The CancellationTokenSource устанавливает состояние ВСЕХ копий токена при вызове Отмена на источнике. См. эту страницу MSDN

Причиной конструкции может быть только вопрос разделения проблем и скорости структуры.

Ответ 5

The CancellationTokenSource - это "вещь", которая по какой-либо причине откладывает отмену. Это необходимо для того, чтобы "отправить" эту отмену во все CancellationToken, которые она выпустила. То, как, например, ASP.NET может отменить операции, когда запрос прерван. Каждый запрос имеет CTSource, который перенаправляет отмену всех маркеров, которые он выдал.

Это отлично подходит для модульного тестирования BTW - создайте свой собственный источник токенов отмены, получите токен, вызовите Отмена на источник и передайте токен вашему коду, который должен обрабатывать отмену.

Ответ 6

Моя "теория" заключается в том, что роль CancellationToken заключается в том, чтобы обеспечить потокобезопасную оболочку, чтобы избежать конкуренции.

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

В то время как с CancellationToken struct у вас есть копия каждой очереди обратных вызовов для каждого потока, поэтому никаких претензий нет.

Это чистая спекуляция, и я могу быть совершенно неправильной и/или могут быть другие веские причины для этого дизайна.

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