Я хочу убить std::thread, используя его объект потока?

Возможный дубликат:
прерывание потока C++0x

Я пытаюсь убить/остановить С++ std::thread, используя его объект потока.

Как мы можем это сделать?


person CPS    schedule 15.12.2012    source источник
comment
Связано: stackoverflow.com/questions/2790346/c0x-thread-interruption   -  person zch    schedule 15.12.2012


Ответы (2)


Ответ @bamboon хорош, однако я считаю, что он заслуживает более сильного утверждения.

Какой бы язык вы ни использовали, ваша программа будет получать и освобождать ресурсы: память, файловые дескрипторы, ... Для простых программ, которые запускаются одним выстрелом, утечка ресурсов не имеет большого значения: когда программа завершается, современные ОС автоматически забирают ресурсы обратно. ; однако для долго работающих программ основным требованием является отсутствие утечки ресурсов или, по крайней мере, повторения.

Поэтому вас с самого начала должны были научить тому, что когда вы приобретаете ресурс, вы должны обеспечить его освобождение в какой-то момент:

void foo(int i) {
    int* array = malloc(sizeof(int) * i);

    /* do something */

    free(array);
}

Итак, задайте себе вопрос:

  • что происходит, когда я убиваю программу?
  • что произойдет, когда я убью нить?

Что ж, как мы уже говорили, когда программа завершается, ОС собирает ресурсы обратно, поэтому предполагается (и это некоторое предположение), что вы не получили ресурс в другой системе ИЛИ что эта система хорошо защищена. против такого злоупотребления, ни вреда, ни фола.

Однако, когда вы завершаете поток, программа продолжает работать, поэтому ОС не собирает ресурсы обратно. У вас произошла утечка памяти, вы заблокировали файл для записи, который вы больше не можете разблокировать, ... Вы не должны убивать темы.

В языках более высокого уровня есть способ справиться с этим: исключения. Поскольку программы в любом случае должны быть защищены от исключений, Java (например) уничтожит поток, приостановив его, бросив исключение в точке выполнения и аккуратно размотав стек. Однако в C++ такой возможности пока нет.

Это невозможно? Нет, очевидно, нет. На самом деле, вы могли бы прекрасно использовать ту же самую идею:

  • encapsulate std::thread, класс interruptible_thread также будет содержать флаг прерывания
  • передайте адрес флага в std::thread при его запуске и сохраните его локальным способом
  • оснастите свой код контрольными точками, где вы проверяете, установлен ли флаг прерывания или нет, и когда он генерирует исключение

Это:

// Synopsis
class interrupt_thread_exception;
class interruptible_thread;
void check_for_interrupt();

// Interrupt exception
class interrupt_thread_exception: public virtual std::exception {
public:
    virtual char const* what() const override { return "interrupt"; }
}; // class interrupt_thread_exception

// Interruptible thread
class interruptible_thread {
public:
    friend void check_for_interrupt();

    template <typename Function, typename... Args>
    interruptible_thread(Function&& fun, Args&&... args):
        _thread([](std::atomic_bool& f, Function&& fun, Args&&... args) {
                    _flag_ref = &f; fun(std::forward<Args>(args)...);
                },
                _flag,
                std::forward<Function>(fun),
                std::forward<Args>(args)...)
    {}

    bool stopping() const { return _flag.load(); }

    void stop() { _flag.store(true); }

private:
    static thread_local std::atomic_bool* _flag_ref = nullptr;

    std::atomic_bool _flag = false;
    std::thread _thread;
}; // class interruptible_thread

// Interruption checker
inline void check_for_interrupt() noexcept(false) {
    if (not interruptible_thread::_flag_ref) { return; }
    if (not interruptible_thread::_flag_ref->load()) { return; }

    throw interrupt_thread_exception();
} // check_for_interrupt

Теперь вы можете просто посыпать свой многопоточный код проверками на прерывание в соответствующих местах.

person Matthieu M.    schedule 15.12.2012
comment
+1, это такая важная концепция. Потоки находятся в другом домене ресурсов и безопасности для процессов. Они требуют кооперативного, а не «состязательного» подхода. Рефакторинг кода, чтобы потоки могли корректно завершаться по запросу. - person Brett Hale; 15.12.2012
comment
void foo(int) уже не является безопасным для исключений, если у вас есть проверка прерывания в середине `/* сделать что-то */', это все равно будет неправильно. - person Jonathan Wakely; 16.12.2012
comment
Какой сигнал? Вы говорите об исключении при прерывании, и некоторые платформы делают то же самое для pthread_cancel. Если возникает исключение, оно не завершает функцию. - person Jonathan Wakely; 16.12.2012
comment
@JonathanWakely: Извините, неправильно понял. Моя точка зрения заключалась в том, что исключения являются нормальным механизмом в языке, поэтому вы можете обслуживать их нормальным способом: try/catch блоки, RAII и т. д., тогда как грубое прерывание оставляет вам не инструмент для очистки. Обратите внимание, что я сказал проверки прерываний в соответствующих местах.! И вы по-прежнему можете прерывать foo, пока вы перехватываете исключение, так что free выполняется... но на самом деле это не имелось в виду, я больше демонстрировал необходимость очистки явно, в то время как Хорошо построенная программа на C++, очевидно, будет использовать RAII. - person Matthieu M.; 16.12.2012
comment
Обратите внимание, что функция terminate() в boost::thread использует в целом аналогичный подход. Их терминология: точки прерывания. Преимущество boost::thread по сравнению с приведенным выше кодом: boost::thread также имеет встроенную поддержку прерывания спящих операций. Например. из boost::thread::sleep_for(). - person Nanno Langstraat; 12.12.2013
comment
Не всегда возможно завершить поток таким образом. Что, если он выполнит некоторую блокировку ввода-вывода, например вызов getline. И нет стандартного способа сделать это неблокирующим способом. - person Simon; 22.03.2014
comment
@Simon: я согласен, но это все же лучший вариант. К сожалению, параллельное выполнение может быть чрезвычайно инвазивным, и остановка потока выполнения является одним из препятствий; Код VM может обойти это, внедрив код (и вызвав исключение, которое разворачивает поток), но собственный код требует взаимодействия. Единственный способ заставить ввод-вывод работать нормально — это стандартизировать способ прерывания ввода-вывода... а этого пока нет :x - person Matthieu M.; 22.03.2014
comment
@MatthieuM. Код, работающий на виртуальной машине, дает вам некоторую гибкость, но не надежность: этот пользовательский код по-прежнему имеет пользовательские инварианты, и у виртуальной машины нет возможности восстановить или даже узнать их. - person curiousguy; 18.01.2020

Вы не можете.

std::threads нельзя прервать. Вы можете использовать boost::thread, который предлагает эту функцию.

Boost делает это, определяя «точки прерывания», на которых завершится поток, если он будет прерван и достигнет такой точки.

Тем не менее, в большинстве случаев размышления о редизайне могут быть самым чистым и простым способом достичь того, чего вы пытаетесь достичь.

Если вы все еще ищете реализацию прерываемых потоков на С++ 11, ознакомьтесь с книгой Энтони Уильямса (владельца потока повышения) "C++ Concurrency in Action". Он проходит базовую реализацию того, как такая вещь может быть достигнута.

std::thread::native_handle дает вам доступ к базовому дескриптору потока для конкретной платформы, который может поддерживать прерывание, однако такой подход делает ваш код непереносимым и, вероятно, никоим образом не чище.

person Stephan Dollberg    schedule 15.12.2012
comment
Прерывание процесса linux, соответствующего высокоуровневому потоку C++, остановит использование ЦП, но никогда не сможет освободить другие ресурсы или восстановить внутренние структуры данных в их предыдущее состояние или даже в любое согласованное состояние, если они изменялись или деформировались. блокировка мьютексов и т. д. - person curiousguy; 18.01.2020