C ++ 0x прерывание потока - PullRequest
55 голосов
/ 07 мая 2010

Согласно окончательному проекту C ++ 0x, нет способа запросить завершение потока. Тем не менее, при необходимости нам нужно реализовать решение «сделай сам».

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

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

Ответы [ 8 ]

16 голосов
/ 09 мая 2010

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

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

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

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

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


Можно также использовать «не использовать исключения для потока управления», но по сравнению с возни с потоками, это всего лишь руководство.

10 голосов
/ 17 мая 2010

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

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

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

6 голосов
/ 15 мая 2010

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

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

6 голосов
/ 07 мая 2010

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

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

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

5 голосов
/ 18 августа 2011

Вот моя скромная реализация средства удаления потоков (для C ++ 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;
}
5 голосов
/ 13 мая 2010

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

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

4 голосов
/ 17 мая 2010

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

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

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

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

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

2 голосов
/ 07 мая 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...