Класс CancellationTokenSource
одноразовый. Быстрый просмотр Reflector доказывает использование KernelEvent
(весьма вероятного) неуправляемого ресурса. Так CancellationTokenSource
как не имеет финализатора, если мы не распорядимся им, сборщик мусора не сделает этого.
С другой стороны, если вы посмотрите на примеры, перечисленные в статье MSDN Отмена в управляемых потоках , только один фрагмент кода избавится от токена.
Как правильно распорядиться этим в коде?
- Вы не можете переносить код, начиная с параллельной задачи,
using
если не ожидаете ее. И имеет смысл иметь отмену, только если вы не ждете. - Конечно, вы можете добавить
ContinueWith
задачу с помощьюDispose
вызова, но так ли это? - А как насчет отменяемых запросов PLINQ, которые не синхронизируются обратно, а просто что-то делают в конце? Скажем
.ForAll(x => Console.Write(x))
? - Это многоразово? Можно ли использовать один и тот же токен для нескольких вызовов, а затем утилизировать его вместе с компонентом хоста, скажем, для управления пользовательским интерфейсом?
Поскольку в нем нет чего-то похожего на Reset
метод очистки IsCancelRequested
и Token
поля, я бы предположил, что его нельзя использовать повторно, поэтому при каждом запуске задачи (или запроса PLINQ) вы должны создавать новую. Это правда? Если да, мой вопрос заключается в том, какова правильная и рекомендуемая стратегия для решения Dispose
этих многочисленных CancellationTokenSource
случаев?
источник
Important: The CancellationTokenSource class implements the IDisposable interface. You should be sure to call the CancellationTokenSource.Dispose method when you have finished using the cancellation token source to free any unmanaged resources it holds.
- docs.microsoft.com/en-us/dotnet/standard/threading/…Я не думаю, что какой-либо из текущих ответов был удовлетворительным. После исследования я нашел этот ответ от Стивена Туба ( ссылка ):
Смелая часть, я думаю, является важной частью. Он использует «более эффектный», что делает его немного расплывчатым. Я интерпретирую это как значение вызова
Dispose
в таких ситуациях, иначе использованиеDispose
не нужно.источник
Я взглянул на ILSpy,
CancellationTokenSource
но могу найти только то,m_KernelEvent
что на самом делеManualResetEvent
является классом-оболочкой дляWaitHandle
объекта. Это должно быть правильно обработано GC.источник
Вы должны всегда распоряжаться
CancellationTokenSource
.Как его распорядиться, зависит именно от сценария. Вы предлагаете несколько разных сценариев.
using
работает только тогда, когда вы используетеCancellationTokenSource
ожидаемую параллельную работу. Если это ваш сенарио, то отлично, это самый простой метод.При использовании задач, используйте
ContinueWith
задачу, как вы указали для утилизацииCancellationTokenSource
.Вы можете использовать plinq,
using
поскольку вы запускаете его параллельно, но ожидаете завершения работы всех параллельно работающих рабочих.Для пользовательского интерфейса вы можете создать новую
CancellationTokenSource
для каждой отменяемой операции, которая не привязана к одному триггеру отмены. Сохраните aList<IDisposable>
и добавьте каждый источник в список, утилизируя все из них, когда ваш компонент будет удален.Для потоков создайте новый поток, который объединит все рабочие потоки и закроет один источник после завершения всех рабочих потоков. См. CancellationTokenSource, когда утилизировать?
Там всегда есть способ.
IDisposable
экземпляры всегда должны быть утилизированы. Сэмплы часто этого не делают, потому что они либо быстрые сэмплы, чтобы показать использование ядра, либо потому, что добавление во всех аспектах демонстрируемого класса было бы слишком сложным для сэмпла. Образец - это просто образец, а не обязательно (или даже обычно) производственный код качества. Не все образцы могут быть скопированы в производственный код как есть.источник
await
задачу и располагать CancellationTokenSource в коде, который идет после ожидания?await
операции, вы можете возобновить работу из-заOperationCanceledException
. Вы могли бы тогда позвонитьDispose()
. Но если есть операции, которые все еще выполняются и используют соответствующиеCancellationToken
, этот токен все еще сообщает о том,CanBeCanceled
что он находится,true
даже если источник утилизирован. Если они пытаются зарегистрировать отмену обратного вызова, BOOM! ,ObjectDisposedException
. Достаточно безопасно позвонитьDispose()
после успешного завершения операции. Это действительно сложно, когда вам нужно что-то отменить.Этот ответ все еще встречается в поиске в Google, и я считаю, что голосование, за которое проголосовали, не дает полной истории. После просмотра исходного кода для
CancellationTokenSource
(CTS) иCancellationToken
(CT) я считаю, что для большинства случаев использования подходит следующая последовательность кода:m_kernelHandle
Внутреннее поле упоминалось выше , является объект синхронизации поддержавWaitHandle
свойство в обоих классах CTS и КТ. Он создается только при доступе к этому свойству. Таким образом, если вы не используетеWaitHandle
какую-либо синхронизацию потоков старой школы в вашемTask
вызове dispose, это не будет иметь никакого эффекта.Конечно, если вы будете использовать его , вы должны делать то , что предлагаются другими ответами выше и задержка вызова ,
Dispose
пока какая - либоWaitHandle
операция с использованием ручки не является полной, поскольку, как описано в API документации Windows , для WaitHandle , результаты не определены.источник
IsCancellationRequested
свойства токена с помощью опроса, обратного вызова или дескриптора ожидания». Другими словами: это может быть не вы (то есть тот, кто делает асинхронный запрос), который использует дескриптор ожидания, это может быть слушатель (то есть тот, кто отвечает на запрос). Это означает, что вы, как ответственный за утилизацию, фактически не можете контролировать, используется ли ручка ожидания или нет.Прошло много времени с тех пор, как я спрашивал об этом и получал много полезных ответов, но я наткнулся на интересную проблему, связанную с этим, и подумал, что опубликую это здесь как еще один ответ:
Вы должны звонить
CancellationTokenSource.Dispose()
только тогда, когда вы уверены, что никто не собирается пытаться получить собственность CTSToken
. В противном случае вы не должны называть это, потому что это гонка. Например, смотрите здесь:https://github.com/aspnet/AspNetKatana/issues/108
В исправлении для этой проблемы код, который ранее делал,
cts.Cancel(); cts.Dispose();
был отредактирован, чтобы просто сделать,cts.Cancel();
потому что любой, кому так не повезло, чтобы попытаться получить токен отмены, чтобы наблюдать его состояние отмены послеDispose
вызова, будет, к сожалению, также должен обрабатыватьObjectDisposedException
- в дополнение кOperationCanceledException
что они планировали.Tratcher сделал еще одно ключевое замечание, относящееся к этому исправлению: «Утилизация требуется только для токенов, которые не будут отменены, так как отмена выполняет все ту же очистку». то есть просто делать
Cancel()
вместо того, чтобы избавляться, действительно достаточно хорошо!источник
Я создал потокобезопасный класс, который связывает a
CancellationTokenSource
с aTask
и гарантирует, чтоCancellationTokenSource
он будет удален послеTask
завершения его работы. Он использует блокировки, чтобы гарантировать, чтоCancellationTokenSource
он не будет отменен во время или после его утилизации. Это происходит для соответствия документации , которая заявляет:А также :
Вот класс:
Основными методами
CancelableExecution
класса являютсяRunAsync
иCancel
. По умолчанию параллельные операции запрещены, это означает, чтоRunAsync
повторный вызов будет автоматически отменен и будет ждать завершения предыдущей операции (если она все еще выполняется) перед началом новой операции.Этот класс может использоваться в приложениях любого типа. Тем не менее, его основное использование в приложениях пользовательского интерфейса, внутри форм с кнопками для запуска и отмены асинхронной операции, или со списком, который отменяет и перезапускает операцию при каждом изменении выбранного элемента. Вот пример первого случая:
RunAsync
Метод принимает дополнительный вCancellationToken
качестве аргумента, который связан с внутренне созданнымCancellationTokenSource
. Поставка этого дополнительного токена может быть полезна в сценариях продвижения.источник