Как завершить поток в C ++ 11?

137

Мне не нужно правильно завершать поток или заставлять его отвечать на команду «завершить». Я заинтересован в принудительном завершении потока с использованием чистого C ++ 11.

Александр V
источник
7
Вот хороший вопрос по этой теме: stackoverflow.com/questions/2790346/c0x-thread-interruption «Все спецификации языка говорят, что поддержка не встроена в язык»
Неманья Борич

Ответы:

138
  1. Вы можете позвонить std::terminate()из любого потока, и поток, на который вы ссылаетесь, принудительно завершится.

  2. Вы можете организовать ~thread()выполнение на объекте целевого потока без вмешательства join()или detach()на этом объекте. Это будет иметь тот же эффект, что и вариант 1.

  3. Вы можете создать исключение, которое имеет деструктор, который генерирует исключение. А затем сделайте так, чтобы целевой поток генерировал это исключение, когда он должен быть принудительно завершен. Сложность в том, чтобы заставить целевой поток генерировать это исключение.

Варианты 1 и 2 не приводят к утечке ресурсов внутри процесса, но завершают каждый поток.

Вариант 3, вероятно, приведет к утечке ресурсов, но он частично совместим, поскольку целевой поток должен дать согласие на создание исключения.

В C ++ 11 нет переносимого способа (о котором я знаю) для некоперативного уничтожения одного потока в многопоточной программе (то есть без уничтожения всех потоков). Не было мотивации создавать такую ​​функцию.

A std::threadможет иметь эту функцию-член:

native_handle_type native_handle();

Возможно, вы сможете использовать это, чтобы вызвать зависимую от ОС функцию, чтобы делать то, что вы хотите. Например, в ОС Apple эта функция существует и native_handle_typeявляется файлом pthread_t. Если вы добьетесь успеха, у вас может произойти утечка ресурсов.

Говард Хиннант
источник
2
Небольшая придирка к вопросу «не допускать утечки внутрипроцессных ресурсов» : хотя, конечно, это правда, что ОС вернет все ресурсы после завершения процесса, но в отношении программы происходит утечка ресурсов . Обычно это не имеет значения, но в некоторых случаях может оставаться проблемой. std::terminateне вызывает статические деструкторы и не очищает выходные буферы, поэтому порядок, в котором освобождаются ресурсы, не определен четко, и у вас нет никакой гарантии, что какие-либо из ваших данных будут видны пользователю или записаны в постоянное хранилище, или даже последовательный и полный.
Дэймон
6
Вы также можете вызвать exit()или abort()с таким же общим эффектом.
нет. местоимения м.
2
# 1 - это шутка, и @ChrisDodd прав. Шутка объясняется в ответе в первом предложении под №3. Также см. Ответ Nanno Langstraat и комментарии под ним.
Ховард
43

Ответ @Howard Hinnant правильный и исчерпывающий. Но это может быть неправильно понято, если оно будет прочитано слишком быстро, потому что std::terminate()(весь процесс) имеет то же имя, что и «завершение», которое имел в виду @AlexanderVX (1 поток).

Резюме: «завершить 1 поток + принудительно (целевой поток не взаимодействует) + чистый C ++ 11 = нет».

Nanno Langstraat
источник
14
Ах, мой номер 1 действительно забавный. Вам не нужно указывать, какой поток вы хотите принудительно завершить. Система просто волшебным образом знает, какой из них вы хотите принудительно завершить, и делает это!
Говард Хиннант
8
Да, std::terminate()ответ похож на классическую озорную историю про джиннов; он выполняет все в желании ОП до буквы, хотя, вероятно, не так, как он имел в виду . Сдержанный юмор заставил меня улыбнуться. :-)
Nanno Langstraat
2
Просто нужно помешать невинным новичкам в C ++ слишком далеко / слишком долго возлагать свои надежды.
Nanno Langstraat 02
2
Элегантно заявлено. :-)
Howard Hinnant
@NannoLangstraat dammnit ... У меня были такие большие надежды, пока я не прочитал: (..lol, ну да ладно, +1 вокруг
:)
13

Этот вопрос на самом деле имеет более глубокий характер, и хорошее понимание концепций многопоточности в целом даст вам представление об этой теме. На самом деле не существует какого-либо языка или операционной системы, которые предоставляли бы вам средства для асинхронного внезапного завершения потока без предупреждения, чтобы не использовать их. И все эти среды выполнения настоятельно рекомендуют разработчикам или даже требуют создания многопоточных приложений на основе кооперативного или синхронного завершения потоков. Причина таких общих решений и советов в том, что все они построены на основе одной и той же общей модели многопоточности.

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

Многопроцессорность предполагает разделение всей среды выполнения на набор полностью изолированных процессов, управляемых операционной системой. Процесс включает и изолирует состояние среды выполнения, включая локальную память процесса и данные внутри нее, а также все системные ресурсы, такие как файлы, сокеты, объекты синхронизации. Изоляция - критически важная характеристика процесса, поскольку она ограничивает распространение неисправностей за пределы процесса. Другими словами, ни один процесс не может повлиять на согласованность любого другого процесса в системе. То же самое верно и для поведения процесса, но в менее ограниченном и более размытом виде. В такой среде любой процесс может быть остановлен в любой «произвольный» момент, потому что, во-первых, каждый процесс изолирован, во-вторых,

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

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

Заратустра
источник
8
Это не очень полезный ответ. Многопоточность здесь не актуальна, и наблюдения о многопоточности довольно обширны.
MSalters
4
Я хотел сказать и подробно сказал, что модель многопоточности не обеспечивает формального способа принудительного завершения потока. C ++ хочет следовать четким моделям, включая модель памяти, модель многопоточности и т. Д. Метод принудительного завершения потока по своей сути небезопасен. Если стандартный комитет C ++ будет вынужден добавить его в C ++, это будет сделано с помощью следующего оператора «Method terminate () завершает выполнение потока. Behavior undefined.», Что будет звучать как «сделать некоторую магию и (возможно) завершить поток» ».
ZarathustrA
В C # есть эта функция.
Дерф Скрен,
@Derf Skren «На самом деле не существует языка или операционной системы, которые предоставляли бы вам возможности для асинхронного внезапного завершения потока без предупреждения, чтобы не использовать их» (c) ZarathustrA «Примечания: Важно! Метод Thread.Abort следует использовать с осторожностью . В частности, когда вы вызываете его для прерывания потока, отличного от текущего, вы не знаете, какой код был выполнен или не выполнен ... "(c) Ссылка Microsoft: docs.microsoft.com/en-us/dotnet/ api /…
ZarathustrA
11

Советы по использованию ОС-зависимой функции для завершения потока C ++:

  1. std::thread::native_handle()только может получить допустимый собственный тип дескриптора потока до вызова join()или detach(). После этого native_handle()вернет 0 - pthread_cancel()будет дамп.

  2. Чтобы эффективно вызвать функцию завершения собственного потока (например pthread_cancel()), вам необходимо сохранить собственный дескриптор перед вызовом std::thread::join()или std::thread::detach(). Так что у вашего родного терминатора всегда есть допустимый родной дескриптор для использования.

Дополнительные объяснения см. По адресу : http://bo-yang.github.io/2017/11/19/cpp-kill-detached-thread .

Boyang
источник
6

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

Определите какое-нибудь глобальное логическое значение:

std::atomic_bool stop_thread_1 = false;

Поместите следующий код (или аналогичный) в несколько ключевых точек таким образом, чтобы все функции в стеке вызовов возвращались до тех пор, пока поток не завершится естественным образом:

if (stop_thread_1)
    return;

Затем, чтобы остановить поток из другого (основного) потока:

stop_thread_1 = true;
thread1.join ();
stop_thread_1 = false; //(for next time. this can be when starting the thread instead)
Дорон Бен-Ари
источник