Як я закінчую нитку в C ++ 11?


137

Мені не потрібно правильно завершувати потік або змушувати його реагувати на команду "завершити". Мені цікаво завершити потік насильно за допомогою чистого C ++ 11.


7
Ось гарне запитання на цю тему: stackoverflow.com/questions/2790346/c0x-thread-interrup "Уся мовна специфікація говорить про те, що підтримка не вбудована в мову"
Nemanja Boric

Відповіді:


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

Відповідь Говарда Гіннанта є правильною та вичерпною. Але це може бути неправильно зрозуміти, якщо він буде прочитаний занадто швидко, тому що std::terminate()(весь процес) має те саме ім'я, що і "закінчується", про який пам'ятав @AlexanderVX (1 тема).

Підсумок: "завершити 1 потік + насильно (цільовий потік не співпрацює) + чистий C ++ 11 = Ні в якому разі."


14
Ах, мій №1 справді смішний. Не потрібно вказувати, яку нитку ви хочете насильно закінчити. Система просто магічно знає, який з них ви хочете насильно закінчити, і робить це!
Говард Хіннант

8
Так, std::terminate()відповідь - це як класична неслухняна історія Джинна; він виконує все в бажанні ОП до листа, хоча, мабуть, не так, як він мав на увазі . Занижений гумор змусив мене посміхнутися. :-)
Nanno Langstraat

2
Просто потрібно не допустити, щоб невинні новачки C ++ не надто довго / занадто довго отримували свої сподівання.
Nanno Langstraat

2
Вишукано заявлено. :-)
Говард Хіннант

@NannoLangstraat dammnit ... У мене були такі великі надії, поки я не прочитав на: (..lol, ой ну + 1 все навколо
:)

13

Це питання насправді має більш глибокий характер і добре розуміння багатопотокових концепцій взагалі дасть вам зрозуміти цю тему. Насправді немає жодної мови чи будь-якої операційної системи, яка б надала вам засоби для асинхронного різкого припинення потоку без попередження про їх використання. І всі ці середовища виконання настійно радять розробнику або навіть вимагають будувати багатопотокові програми на основі спільного або синхронного завершення потоку. Причиною цих загальних рішень та порад є те, що всі вони побудовані на основі тієї ж загальної багатопотокової моделі.

Порівняємо багатопроцесорні та багатопотокові концепції, щоб краще зрозуміти переваги та обмеження другого.

Багатопроцесорна система передбачає розщеплення всього середовища виконання на безліч повністю ізольованих процесів, керованих операційною системою. Процес включає і ізолює стан середовища виконання, включаючи локальну пам'ять процесу та даних всередині нього та всі системні ресурси, такі як файли, сокети, об'єкти синхронізації. Ізоляція є критично важливою характеристикою процесу, оскільки вона обмежує поширення несправностей за межами процесу. Іншими словами, жоден процес не може впливати на послідовність будь-якого іншого процесу в системі. Те саме стосується поведінки процесу, але менш обмеженим та більш розмитим способом. У такому середовищі будь-який процес може бути вбитий у будь-який "довільний" момент, оскільки, по-перше, кожен процес ізольований, по-друге,

На відміну від цього, багатопотоковість передбачає запуск декількох потоків в одному процесі. Але всі ці потоки мають одне і те ж ізоляційне вікно, і немає ніякого контролю операційною системою внутрішнього стану процесу. В результаті будь-яка нитка здатна змінити глобальний стан процесу, а також зіпсувати його. У той же момент точки, в яких стан потоку, як відомо, є безпечним для вбивства потоку, повністю залежать від логіки програми і не відомі ні для операційної системи, ні для часу програмування мови програмування. В результаті припинення потоку в довільний момент означає вбивство його в довільній точці шляху його виконання і може легко призвести до пошкодження даних, пам’яті та витоку даних,

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


8
Це не дуже корисна відповідь. Мультиобробка тут не актуальна, і спостереження щодо багатопотокової обробки досить широкі.
MSalters

4
Що я хотів сказати і детально сказати, це те, що багатопотокова модель не забезпечує формального способу насильницького завершення потоку. C ++ хоче слідувати чітким моделям, включаючи модель пам'яті, багатопоточну модель тощо. Метод примусового завершення потоку по суті є небезпечним. Якщо стандартний комітет C ++ буде змушений додавати його до C ++, він буде зроблений із наступним твердженням "Метод термінації () припиняє виконання потоку. Поведінка не визначено". Це буде звучати як "зробити деяку магію і (можливо) припиняє потік" ".
ЗаратустраA

C # має цю особливість.
Derf Skren

@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()буде coredump.

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

Більше пояснень див. На веб-сторінці: http://bo-yang.github.io/2017/11/19/cpp-kill-detached-thread .


5

Я думаю, що нитка, яку потрібно вбити, знаходиться або в будь-якому режимі очікування, або виконує якусь важку роботу. Я б запропонував використовувати «наївний» спосіб.

Визначте деякі глобальні булеві:

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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.