Прерывание потока
Иногда поток полезно прервать до его нормального завершения. Например, отладчику может понадобиться прервать вышедший из-под контроля поток. После прерывания поток удаляется из системы и не может быть начат снова. Для прерывания потока до его нормального завершения служит метод Abort(). Ниже приведена простейшая форма этого метода:
Метод Abort() создает необходимые условия для генерирования исключения ThreadAbortException в том потоке, для которого он был вызван. Это исключение приводит к прерыванию потока и может быть перехвачено и в коде-программы, но в этом случае оно автоматически генерируется еще раз, чтобы остановить поток. Метод Abort() не всегда способен остановить поток немедленно, поэтому если поток требуется остановить перед тем, как продолжить выполнение программы, то после метода Abort() следует сразу же вызвать метод Join().
Кроме того, в самых редких случаях методу Abort() вообще не удается остановить поток. Это происходит, например, в том случае, если кодовый блок finally входит в бесконечный цикл. В приведенном ниже примере программы демонстрируется применение метода Abort() для прерывания потока:

Метод Abort() не следует применять в качестве обычного средства прерывания потока, поскольку он предназначен для особых случаев. Обычно поток должен завершаться естественным образом, чтобы произошел возврат из метода, выполняющего роль точки входа в него.
В некоторых случаях оказывается полезной другая форма метода Abort (), приведенная ниже в общем виде:
где stateInfo обозначает любую информацию, которую требуется передать потоку, когда он останавливается. Эта информация доступна посредством свойства ExceptionState из класса исключения ThreadAbortException. Подобным образом потоку можно передать код завершения.
Запрос на преждевременное прерывание может быть переопределен в самом потоке. Для этого необходимо сначала перехватить в потоке исключение ThreadAbortException, а затем вызвать метод ResetAbort(). Благодаря этому исключается повторное генерирование исключения по завершении обработчика исключения, прерывающего данный поток. Ниже приведена форма объявления метода ResetAbort():
Вызов метода ResetAbort() может завершиться неудачно, если в потоке отсутствует надлежащий режим надежной отмены преждевременного прерывания потока. Давайте модифицируем вышеуказанный пример:
Как прервать, приостановить и возобновить потоки в C #?
Как прервать, приостановить и возобновить потоки в C #?
задан 28 июн ’09, 05:06
(см. обновление моего ответа по вашему комментарию) — Marc Gravell♦
4 ответы
В общем, не приостановить (или прервать) Thread — обычно вы не можете сказать, что он делает, и это может привести к разного рода проблемам с блокировкой, таким как блокировка, которая не снята, или инициализатор типа (статический конструктор), который остается зависшим.
Однако вы можете приостановить код с помощью более элегантных методов — например, используя ManualResetEvent в вашем цикле этот внешний код (в другом потоке) может открываться и закрываться:
Затем другой поток с доступом (возможно, косвенным) может приостановить ( Reset ) и возобновить ( Set ) рабочий, но зная, что он только приостанавливает работу в безопасном состоянии.
Повторите свой комментарий (на другой ответ) — похоже, у вас есть читатель и писатель; идеально подходит для сценария производителя / потребителя. У меня есть пример по этому вопросу о том, как написать производителя / потребителя с ограниченным размером (чтобы он не заболачивался, если потребитель работает медленно) — потребитель блокирует, если данных нет, и производитель блокирует, если их слишком много.
многопоточность — Как прекратить или остановить отдельный поток в C ++?
Я заинтересован в прекращении / остановке / уничтожении отдельного потока в C ++. Как это может быть сделано?
Решение
Нет положений, чтобы остановить другой поток; будь то отдельный или присоединяемый.
Единственный способ остановить поток — это возврат потока из начальной функции потока.
В этом конкретном случае я бы предложил следующие изменения:
- Не отсоединяйте нить. Создайте его в main ().
- Добавьте значение bool и std::mutex bool инициализируется как false
- Каждый раз через внутренний цикл потока блокируйте мьютекс, используя std::unique_lock , возьмите значение bool, затем разблокируйте мьютекс. После разблокировки мьютекса, если бул был true , вырваться из цикла и вернуться.
- В main () перед выходом: заблокируйте мьютекс, установите флаг bool в true , разблокировать мьютекс, затем присоединиться к потоку
Это не идеально, так как вторая нить проверит флаг bool и вернется до пяти секунд. Но это был бы первый теп.
Другие решения
Вы можете опуститься ниже C ++ Standard и использовать специфичные для ОС функции, такие как отправка собственного процесса сигнала при настройке маски сигнала, чтобы он доставлялся только в отдельный поток — обработчик может установить флаг, опрашиваемый из вашего потока. Если ваша основная процедура ждет дольше, чем период опроса плюс немного, вы можете догадаться, что она должна была завершиться ;-P. Та же самая общая идея может быть использована с любым другим механизмом сигнализации, таким как атомная переменная флага terminate-asap.
В качестве альтернативы, и только в крайнем случае, есть pthread_cancel и тому подобное. Обратите внимание, что асинхронное аннулирование, подобное этому, является в общем-то опасной вещью, которую нужно делать в целом — вы должны быть осторожны, чтобы завершаемый поток не мог находиться в каком-либо коде с заблокированными / захваченными ресурсами, или у вас могут быть тупики, утечки и / или неопределенное поведение , Например, ваш код звонков std::this_thread::sleep_for(std::chrono::seconds(5)); — что делать, если после истечения этого интервала ОС запрашивает обратный вызов, но функция, которую необходимо продолжить, использует стек завершенного потока? Примером того, где это может быть безопасно, является то, что поток выполняет какое-то простое сокращение числа в цикле в вашем приложении.
В противном случае ответ Сэма документирует альтернативу, если вы избегаете отсоединять нить в первую очередь …
Нет способа полностью отключить отдельную нить. Это потребует ожидания завершения очистки, и вы можете сделать это только в том случае, если поток является присоединяемым.
Рассмотрим, например, если поток содержит мьютекс, который должен получить другой поток, чтобы полностью завершить работу. Единственный способ полностью закрыть этот поток — заставить его освободить этот мьютекс. Это потребует сотрудничества потока.
Подумайте, открыл ли поток файл и удерживает ли он этот файл. Прерывание потока оставит файл заблокированным до завершения процесса.
Подумайте, содержит ли поток мьютекс, который защищает некоторое общее состояние и временно перевел это общее состояние в несогласованное состояние. Если вы прервите поток, либо мьютекс никогда не будет освобожден, либо мьютекс будет освобожден с защищенными данными в несогласованном состоянии. Это может вызвать сбои.
Завершение потока из другого потока
![]()
Красивее всего это делается через механизм CancellationToken . Этот механизм специально создан для передачи сообщений об остановки и отмене между потоками:
Здесь я использую ожидание на событии как прерываемую альтернативу Thread.Sleep . Если ожидание успешно — значит, поток надо останавливать. Если неуспешно (тайм-аут ожидания) — значит, можно продолжать работу.
Перед ожиданием я проверяю IsCancellationRequested , чтобы не создавать событие ядра когда этого не требуется.
Создание такого потока:
Также вместо потока можно использовать задачу ( Task.Run ). В таком случае для ожидания лучше использовать Task.Delay , это позволит обойтись вовсе без объектов ядра:
Бонусом к использованию CancellationToken идет возможность передачи ct дальше в doAnything — благодаря чему вы можете тонко выбирать, в какие моменты допустимо прерывание обработки.
Если вы будете использовать асинхронный вариант — то практически все асинхронные функции стандартной библиотеки также умеют принимать CancellationToken , что позволяет безопасно прервать любую долгую операцию, если вы того пожелаете.