С++ 0x прерывание потока

В соответствии с окончательным проектом С++ 0x нет способа запросить завершение потока. При этом, если необходимо, нам нужно реализовать самодельное решение.

С другой стороны boost:: thread обеспечивает механизм для прерывания потока в безопасном режиме.

На ваш взгляд, какое лучшее решение? Разработка собственного кооперативного механизма прерывания или переход на родину?

Ответ 1

Все спецификации языка говорят, что поддержка не встроена в язык. boost::thread::interrupt также нуждается в некоторой поддержке функции потока:

Когда прерванный поток затем выполняет одну из указанных точек прерывания (или если он в настоящий момент заблокирован во время выполнения)

то есть. когда функция потока не дает вызывающему абоненту возможности прервать, вы все еще застреваете.

Я не уверен, что вы имеете в виду с "going native" - нет встроенной поддержки, если вы не заклинаем boost:threads.

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


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

Ответ 2

Использование встроенного дескриптора для отмены потока является плохой опцией на С++, так как вам нужно уничтожить все выделенные стеком объекты. Это была основная причина, по которой они не включали операцию отмены.

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

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

Ответ 3

Небезопасно останавливать поток, поскольку в данный момент вы не контролируете состояние каких-либо структур данных.

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

Если вы просто хотите дождаться окончания потока, используйте join() или будущее.

Ответ 4

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

Если бы вы были родными, вы бы ничего не выиграли; вы просто выкинули бы все преимущества стандартного и кросс-платформенного механизма нарезки OOP. Чтобы ваш код был правильным, потоку необходимо было бы свернуть совместно, что подразумевает связь, описанную выше.

Ответ 5

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

Решение boost::thread::interrupt работает, прося красиво. Он будет прервать поток, делающий что-то прерывистое, например, ожидая переменную условия Boost.Thread или если поток выполняет одно из этих действий после вызова прерывания. Даже тогда нить не бесцеремонно переносится через мясорубку, поскольку, скажем, функция Win32 TerminateThread, она просто вызывает исключение, которое, если бы вы были хорошо себя зарекомендовавшим кодером и использовали RAII всюду, очистит после себя и изящно выйти из потока.

Ответ 6

Вот моя смиренная реализация обработчика потоков (для С++ 0x). Надеюсь, это будет полезно.

// Class cancellation_point
#include <mutex>
#include <condition_variable>

struct cancelled_error {};

class cancellation_point
{
public:
    cancellation_point(): stop_(false) {}

    void cancel() {
        std::unique_lock<std::mutex> lock(mutex_);
        stop_ = true;
        cond_.notify_all();
    }

    template <typename P>
    void wait(const P& period) {
        std::unique_lock<std::mutex> lock(mutex_);
        if (stop_ || cond_.wait_for(lock, period) == std::cv_status::no_timeout) {
            stop_ = false;
            throw cancelled_error();
        }
    }
private:
    bool stop_;
    std::mutex mutex_;
    std::condition_variable cond_;
};


// Usage example
#include <thread>
#include <iostream>

class ThreadExample
{
public:
    void start() {
        thread_ = std::unique_ptr<std::thread>(
            new std::thread(std::bind(&ThreadExample::run, this)));
    }
    void stop() {
        cpoint_.cancel();
        thread_->join();
    }
private:
    void run() {
        std::cout << "thread started\n";
        try {
            while (true) {
                cpoint_.wait(std::chrono::seconds(1));
            }
        } catch (const cancelled_error&) {
            std::cout << "thread cancelled\n";
        }
    }
    std::unique_ptr<std::thread> thread_;
    cancellation_point cpoint_;
};

int main() {
    ThreadExample ex;
    ex.start();
    ex.stop();
    return 0;
}

Ответ 7

Моя реализация потоков использует идиому pimpl, и в классе Impl у меня есть одна версия для каждой поддерживаемой ОС, а также одна, которая использует boost, поэтому я могу решить, какой из них использовать при создании проекта.

Я решил сделать два класса: один - это Thread, который имеет только базовые сервисы, предоставляемые ОС; а другой - SafeThread, который наследует от Thread и имеет метод совместного прерывания.

Thread имеет метод terminate(), который выполняет навязчивое завершение. Это виртуальный метод, который перегружен в SafeThread, где он сигнализирует объект события. Существует метод (статический) yeld(), который время от времени должен вызывать текущий поток; этот метод проверяет, сигнализирован ли объект события, и, если да, выбрасывает исключение, пойманное на вызывающей стороне точки ввода потока, тем самым завершая поток. Когда он это делает, он сигнализирует второй объект события, поэтому вызывающий объект terminate() может знать, что поток был безопасно остановлен.

В случаях, когда существует опасность взаимоблокировки, SafeThread:: terminate() может принимать параметр таймаута. Если истечение времени ожидания истекает, он вызывает Thread:: terminate(), тем самым убивая интрузивно поток. Это последний ресурс, когда у вас есть что-то, что вы не можете контролировать (например, сторонний API) или в ситуациях, когда тупик наносит больше урона, чем утечки ресурсов и т.п.

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

Ответ 8

Я согласен с этим решением. Например,.NET позволяет прервать любой рабочий поток, и я никогда не использую эту функцию и не рекомендую делать это любому профессиональному программисту. Я хочу решить, когда рабочий поток может быть прерван, и каков способ сделать это. Он отличается от аппаратного обеспечения, ввода-вывода, интерфейса и других потоков. Если поток может быть остановлен в любом месте, это может вызвать поведение программы undefined при управлении ресурсами, транзакциях и т.д.