Как завершить или остановить отсоединенный поток в c++?

меня интересует завершение/остановка / убийство отсоединенного потока в c++. Как это сделать?

void myThread()
{
    int loop = 0;
    while(true)
    {
        std::this_thread::sleep_for(std::chrono::seconds(5));
        ++loop;
    }
}

void testThread()
{
    std::thread globalThread(myThread);
    globalThread.detach();
}

int main(void)
{
    testThread();
    for(unsigned int i=0; i < 1000; i++)
    {
        cout << "i = " << i << endl;
    }
    return 0;
}

причина, по которой я хотел бы "остановить"/"завершить" globalThread (), заключается в том, что valgrind перечисляет, что это "возможно потерянный" тип утечки памяти (152 байта). Каков наилучший способ справиться с этим?

4 ответов


нет положений, чтобы остановить другой поток; будь то отсоединенный или присоединяемый.

единственный способ остановить поток-это вернуть поток из начальной функции потока.

в данном конкретном случае, я бы предложил следующие изменения:

  1. не отрывать нить. Создайте экземпляр в main ().
  2. добавить значение bool, и std::mutex, bool инициализируется в false
  3. каждый раз через внутренняя петля потока, заблокируйте мьютекс с помощью std::unique_lock, возьмите значение bool, затем разблокируйте мьютекс. После разблокировки мьютекса, если bool был true, вырваться из петли, и вернуться.
  4. в main (), перед выходом: заблокируйте мьютекс, установите флаг bool в true, разблокировать мьютекс, а затем присоединиться к потоку

Это не идеально, так как для второго потока потребуется до пяти секунд, чтобы проверить флаг bool и вернуться. Но, это будет первый теп.


Вы можете опуститься ниже стандарта C++ и использовать специфические для ОС функции, такие как отправка вашего собственного процесса сигнала при настройке маски сигнала, чтобы он доставлялся только в отдельный поток-обработчик может установить флаг, который опрашивается из вашего потока. Если ваша основная процедура ждет дольше, чем период опроса плюс немного, вы можете догадаться, что она должна была закончиться ;-P. та же общая идея может быть использована с любым другим сигнальным механизмом, таким как атомарный флаг terminate-asap переменная.

кроме того, и только в крайнем случае, есть pthread_cancel и тому подобное. Обратите внимание, что асинхронная отмена, как это, является лихо опасной вещью в целом - вы должны быть осторожны, что поток, который вы завершаете, не может быть в любом коде с заблокированными/взятыми ресурсами, или у вас могут быть тупики, утечки и/или неопределенное поведение. Например, ваш код вызывает std::this_thread::sleep_for(std::chrono::seconds(5)); - что делать, если это запрашивает у ОС обратный вызов по истечении интервала, но функция продолжить после этого использовать стек завершенного потока? Пример, где это может быть безопасно, - это если поток делает какое-то простое число в цикле в вашем приложении.

в противном случае ответ Сэма документирует альтернативу, если вы избегаете отсоединения потока в первую очередь....


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

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

рассмотрим, имеет ли поток открыл файл и заблокировал его. Прерывание потока приведет к блокировке файла до завершения процесса.

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

вам нужно сотрудничество потока чтобы чисто закрыть его.


Если это код Windows (скажем, в MSVC), вы можете вызвать функцию TerminateThread (), и это убьет поток, однако сначала вы должны проверить, безопасно ли убивать потоки, вы не хотите оставлять поврежденные / наполовину записанные данные, лежащие вокруг:)

Edit: чтобы обработать точку и объяснить, как это может быть полезно в контексте утечек памяти, если поток выделяет кучу, то завершение потока оставит данные кучи несвободными, однако даже если вы используете кучу для потока, вы можете получить доступ к этой куче из других потоков / процессов в приложении и освободить ее извне потока, когда вы завершите ее через вызов функции TerminateThread. Или, в качестве альтернативы, вы можете создать приложение так, чтобы куча поддерживалась другим потоком, чтобы отсоединенный поток использовал только стек и заимствовал доступ к куче, которая уже есть, тогда нет возможности утечки памяти внутри отсоединенный поток, даже если поток явно убит. Теоретически вы также можете получить блокировку кучи под windows, хотя есть несколько способов обойти это, например, явным образом разблокировав эту часть кучи после завершения потока с помощью системного вызова низкой библиотеки и вставив свою собственную блокировку вокруг этой области кучи, чтобы предотвратить другие потоки от снятия блокировки во время ее разблокировки - т. е. двойной замок, который может быть заблокирован только и только разблокирован родительским процессом, но любые другие потоки ждут разблокировки перед доступом к этой области кучи (если они вообще ее используют). Красивая вещь о потоках заключается в том, что они разделяют кучу, Поэтому есть много способов решить эту проблему, с или без убийства потоков.