К сожалению, не может быть лучшего варианта.Это действительно зависит от вашего конкретного сценария.Идея состоит в том, чтобы аккуратно остановить поток в безопасных точках.В этом суть причины, по которой Thread.Abort
нехорошо;потому что это не гарантируется в безопасных точках.Посыпая код механизмом остановки, вы эффективно вручную определяете безопасные точки.Это называется совместное аннулирование.Есть в основном 4 широких механизма для этого.Вы можете выбрать тот, который наилучшим образом соответствует вашей ситуации.
Опрос флага остановки
Вы уже упоминали этот метод.Это довольно распространенный.Периодически проверяйте флаг в безопасных точках вашего алгоритма и спасайтесь, когда он получает сигнал.Стандартный подход заключается в маркировке переменной volatile
.Если это невозможно или неудобно, вы можете использовать lock
.Помните, вы не можете пометить локальную переменную как volatile
, поэтому, если лямбда-выражение захватывает ее, например, через замыкание, вам придется прибегнуть к другому методу для создания необходимого барьера памяти.Нет ничего другого, что нужно сказать об этом методе.
Используйте новые механизмы отмены в TPL
Это похоже на опрос флага остановкиза исключением того, что он использует новые структуры данных отмены в TPL.Это все еще основано на совместных шаблонах отмены.Вам необходимо получить CancellationToken
и периодически проверять IsCancellationRequested
.Чтобы запросить отмену, вы должны позвонить Cancel
на CancellationTokenSource
, который первоначально предоставил токен.С новыми механизмами отмены можно многое сделать.Вы можете прочитать больше о здесь .
Использовать дескрипторы ожидания
Этот метод может быть полезен, если ваш рабочий поток требует ожидания через определенный интервал илидля сигнала во время его нормальной работы.Например, вы можете Set
a ManualResetEvent
, чтобы сообщить потоку, что пора остановиться.Вы можете проверить событие, используя функцию WaitOne
, которая возвращает bool
, указывающую, было ли событие сигнализировано.WaitOne
принимает параметр, который указывает, сколько времени ждать возврата вызова, если событие не было сигнализировано за это время.Вы можете использовать эту технику вместо Thread.Sleep
и одновременно получать указание остановки.Это также полезно, если есть другие WaitHandle
экземпляры, которые поток может ждать.Вы можете позвонить WaitHandle.WaitAny
, чтобы дождаться любого события (включая событие остановки), все в одном вызове.Использование события может быть лучше, чем вызов Thread.Interrupt
, поскольку у вас больше контроля над потоком программы (Thread.Interrupt
выдает исключение, поэтому вам придется стратегически размещать блоки try-catch
для выполнения любой необходимой очистки).
Специализированные сценарии
Существует несколько одноразовых сценариев с очень специализированными механизмами остановки.Этот ответ определенно выходит за рамки перечисления их всех (не говоря уже о том, что это было бы почти невозможно).Хороший пример того, что я имею в виду, это класс Socket
.Если поток заблокирован при вызове Send
или Receive
, то вызов Close
прервет сокет при любом блокирующем вызове, в котором он был, чтобы эффективно разблокировать его.Я уверен, что есть несколько других областей в BCL, где аналогичные методы могут быть использованы для разблокировки потока.
Прервать поток с помощью Thread.Interrupt
Преимущество здесь в том, что это просто, и вам не нужно сосредотачиваться на том, чтобы посыпать свой код чем-то действительно. Недостатком является то, что у вас мало контроля над безопасными точками в вашем алгоритме. Причина в том, что Thread.Interrupt
работает, внедряя исключение в один из стандартных вызовов блокировки BCL. К ним относятся Thread.Sleep
, WaitHandle.WaitOne
, Thread.Join
и т. Д. Таким образом, вы должны быть мудрыми в том, где вы их размещаете. Однако, в большинстве случаев алгоритм определяет, куда они идут, и в любом случае это нормально, особенно если ваш алгоритм проводит большую часть своего времени в одном из этих блокирующих вызовов. Если ваш алгоритм не использует один из блокирующих вызовов в BCL, тогда этот метод не будет работать для вас. Теория здесь такова, что ThreadInterruptException
генерируется только из ожидающего вызова .NET, поэтому вероятно в безопасной точке. По крайней мере, вы знаете, что поток не может находиться в неуправляемом коде или выйти из критической секции, оставляя висячий замок в полученном состоянии. Несмотря на то, что он менее инвазивен, чем Thread.Abort
, я все еще не одобряю его использование, поскольку неясно, какие вызовы отвечают на него, и многие разработчики не будут знать о его нюансах.