Повторное использование потока - поведение boost vs std thread

Кажется, я получаю другое поведение назначения объекта потока между потоками boost и std. Если я использую потоки ускорения, переменную члена потока можно переназначить, а поток воссоздать. Если я использую потоки std, я получаю ошибку времени выполнения terminate called without an active exception.

Вот код, о котором идет речь (запустите, затем замените std :: на boost: :)

class ThreadTest 
 {
    private:
    std::thread mythread;
    std::atomic<bool> running_;

    int doSomeWork(){
        int i=0;
        cout << "starting" << endl;
        while(running_){
            cout << "working" << endl;
            std::this_thread::sleep_for (std::chrono::seconds(1));
            if (i>3){ break; } else { i++; }
        }
        running_ = false;
    }

    public:
    void runThread(){
        running_ = true;
        mythread = std::thread(&ThreadTest::doSomeWork, this);
    }

    void joinThread(){
        mythread.join();
    }
 };

int main(){ 
    ThreadTest test;
    test.runThread();
    std::this_thread::sleep_for (std::chrono::seconds(10));
    test.runThread();
    test.joinThread();
    return 0;
 }

Выход для наддува:

starting
working
working
working
working
working
starting
working
working
working
working
working

Вывод для std ::

starting
working
working
working
working
working
terminate called without an active exception
Aborted (core dumped)

Этот конкретный фрагмент кода используется в библиотеке, которая, похоже, не имеет повышения как зависимости. Я хотел бы сохранить это таким образом, так есть ли способ получить поведение повышения «переназначения» с помощью потоков std?

РЕДАКТИРОВАТЬ - РЕШЕНИЕ

Я добавил std::atomic<bool> threadInitialized_; в класс, для которого задано значение true в функции потока doSomeWork(). Мой метод runThread() становится:

void runThread(){
    if (threadInitialized_)
       mythread.join();

    running_ = true;
    mythread = std::thread(&ThreadTest::doSomeWork, this);
}

Я знаю, что это заблокирует основной поток, пока не будет создан порожденный поток.

Что заставляет вас думать, что std::thread можно использовать повторно?

Sid S 29.11.2018 05:39

@SidS Я хотел бы знать, есть ли способ повторно использовать его, а если нет, то какой эквивалентный способ std :: thread делать то, что я пытаюсь сделать

aleksk 29.11.2018 05:44

Поскольку boost 1.50, boost::thread и std::thread должны вести себя в этом отношении одинаково. Существует макрос препроцессора, который вы можете определить, чтобы вернуться к старому поведению, но он не используется по умолчанию.

Miles Budnek 29.11.2018 05:52

Помимо технических проблем, есть еще одна вещь, которая вас укусит: похоже, у вас несбалансированные вызовы runThread () и joinThread (), и вы ожидаете, что это сработает! Для меня это показатель того, что ваш код смешивает обязанности. Если этот класс отвечает за поддержку фоновой службы, возложите на него полную ответственность за это. Игнорировать запросы на запуск, когда службы работают, и игнорировать запросы на завершение работы, если это не так. Между тем, необходимый перезапуск потока должен оставаться обязанностью этого класса, а не кого-либо еще.

Ulrich Eckhardt 29.11.2018 07:26

@UlrichEckhardt joinThread () существует только для целей этого примера, иначе моя основная функция вернется, и я не получу вывода. Спасибо за ваш вклад, может быть, он пригодится кому-то другому! В моем реальном коде я проверяю, запущен ли уже поток, и игнорирую запрос, если он есть.

aleksk 29.11.2018 07:29
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
5
309
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

От std :: thread :: operator = ()

«Если [объект потока] может быть присоединен, вызывается terminate ()».

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

aleksk 29.11.2018 05:50

Перед повторным использованием объекта std::thread необходимо остановить или отсоединить поток.

Miles Budnek 29.11.2018 05:51

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

Sid S 29.11.2018 05:51

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

aleksk 29.11.2018 05:58

Как правило, как правильно указано выше, все (присоединяемые) потоки должны быть объединены или отсоединены до того, как их объект будет уничтожен.

Одно (из многих) различий между потоками Boost и std :: thread заключается в том, что потоки Boost отделяются внутри своего деструктора, чего не делают std :: thread; поэтому ваше неправильное использование std :: thread правильно запускает terminate ().

PS: НЕ (!!) верьте другим комментаторам выше, что std :: thread и boost :: thread должны вести себя «одинаково» - это просто неправда!

«Потоки Boost отключаются внутри своего деструктора» - эта функция теперь устарела, см. boost.org/doc/libs/1_66_0/doc/html/thread/….

Daniel Langr 29.11.2018 07:23

Приведите доказательства. Связанная документация говорит об обратном. (Цитата: Причина перехода на std::terminate заключается в том, что либо неявное отключение, либо присоединение к потоку joinable() в его деструкторе может привести к трудным для отладки ошибкам корректности (для detach) или производительности (для join), возникающим только при возникновении исключения. Таким образом, программист должен гарантировать, что деструктор никогда не будет выполняться, пока поток все еще может быть присоединен. Присоединитесь к цепочке перед удалением или используйте цепочку с заданной областью действия.).

Daniel Langr 29.11.2018 07:33

При использовании boost :: thread, если вы явно не вызываете join () или detach (), тогда деструктор boost :: thread и оператор присваивания вызовут detach () для объекта потока, который уничтожается / назначается соответственно. Для объекта std :: thread C++ 11 это приведет к вызову std :: terminate () и прерыванию приложения. В этом случае вам нужно вызвать detach () или join () вручную.

Другие вопросы по теме