LINUX.ORG.RU

Корректно прервать std::thread

 ,


0

3

Добрый всем вечер!

Есть такой класс:

class BackgroundTask
{
public:
    BackgroundTask();
    ~BackgroundTask();

    void odd_job()
    {
        while (1)
        {
            // do something useful
        }
    }

    void slot_need_stop()
    {
        std::terminate();
    }
};

Где-то в main:

    std::thread jobt(&BackgroundTask::odd_job, &job);

    jobt.join();

Слот slot_need_stop связан с сигналом от обработчика SIGTERM и прерывает работу odd_job.

Насколько корректно такое прерывание потока? Если нет, то посоветуйте, плиз, как. Заранее благодарен.


Насколько корректно такое прерывание потока

Нинасколько. В pthread бай дизайн нет возможности принудительно прервать работу одного потока. std::terminate() аварийно завершает работу процесса целиком.

Если нет, то посоветуйте, плиз, как

Либо через std::jthread::request_stop() (C++20), либо руками через общую переменную.

intelfx ★★★★★
()
Последнее исправление: intelfx (всего исправлений: 2)

то посоветуйте, плиз, как

Вместо while (1) сделай while (_running). _running сделай атомиком, например std::atomic_bool _running;. Его и переключай в slot_need_stop.

ox55ff ★★★★★
()

join-able (те которые НЕ PTHREAD_DETACHED) нитки нехорошо убивать снаружи. Совсем нехорошо, вот прямо вообще.

Ты-же не знаешь где-там, кто и чего ждал от join. Только те которые detached (не join-able) можно окрестясь прибить через pthread_cancel;

и то бабушка надвое сказала - не во всех местах нитку можно прервать cancel`ом и соотв.флаг должен быть взведён и что-то как-то что-то надо делать с ресурсами нити.

КОРОЧЕ: управлять нитями можно только через atomic или память овеянную mutex. Если архитектурно нитку можно завершать снаружи, значит снаружи неё должен быть atomic на который она умеет реагировать и и нормально завершатся. pthread_cancel - это уже отработка аварийных ситуаций.

ещё короче: системные треды зло :-) пожирают ресурс, тормозят и плохо управляются. На прикладном уровне полно всяких других green-thread,fibers,coroutines.

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

Мне, вообще-то, надо (пока) просто остановить поток. Я сделал как сказал @ox55ff, и все получилось. А вот про green-thread и пр. почитаю. Да и к рекомендациям прислушаюсь.

Всем спасибо!

braboar
() автор топика
Ответ на: комментарий от MKuznetsov

У меня исторически сложилось, что я гораздо лучше управляюсь с QThread, чем с std::thread. Мне пригодилась бы рекомендация, как организовать обмен между потоками.

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

braboar
() автор топика
Ответ на: комментарий от braboar

про то что речь про Qt было понятно по «слотам» :-)

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

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

А вообще сигнал от таймера мастера ко всем воркерам, он зачем ? вот вы физически как себе его цель представляете ?? «дан свисток и все бросились конкурировать за ядра ЦП, победитель получает розовую пони» ?

MKuznetsov ★★★★★
()
Ответ на: комментарий от braboar

современные однопоточные приложение работают *очень* быстро. У нас очень шустрая аппаратура в этом плане. Поэтому по мере возможности придерживаться местного event-loop.

Qt значит сигналы-слоты (какие они там сейчас), glib своё и прочие-прочие (lib ev, lib uv, etc). Вот пусть они занимаются IO и таймингами в том числе. Они все реально очень-очень-очень шустрые, а если не мигрировать по ядрам и не сбивать кеши то вообще пулемёты :-)

За «можно я порожу thread», надо писать письмо товарищу майору архитектору с детальным обоснованием. И скорее всего откажут и это правильно. Максимум разделить сетевые службы, местный IO и тупо математика. Так вот, оставаясь внутри своего избранного фреймворка ты это и делаешь;

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

MKuznetsov ★★★★★
()
Ответ на: комментарий от braboar

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

Таймер дёргает метод notify_all у объекта std::condition_variable. А потоки спят на методе wait этого объекта.

Но это если потокам больше ничего не нужно делать кроме как дожидаться сигнала от таймера. Иначе спать на wait нельзя. В этом случае просто будет другой подход.

ox55ff ★★★★★
()
Ответ на: комментарий от braboar

Если я правильно понял, то можно сделать примерно так:

Под мьютексом складывай сообщения в очередь. Вместо таймера поток внутри цикла while спит на std::this_thread::sleep_for время равное периоду таймера. Когда поток просыпается он берёт мьютекс, достаёт из очереди сообщения, пишет их в журнал, освобождает мьютекс и снова засыпает на sleep_for.

ox55ff ★★★★★
()
Ответ на: комментарий от rumgot

Сейчас подумал и в предложенной мной схеме есть минус — если нужно остановить поток, то придётся ждать до окончания сна. Критично, если интервалы большие. С условной переменной можно и по таймауту выйти из ожидания, так и по сигналу, если нужно завершить поток до окончания таймаута. Можно попробовать и с std::condition_variable::wait_for.

ox55ff ★★★★★
()
Ответ на: комментарий от ox55ff

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

Если можно, с этого места поподробней: что понимается под сигналом и как его обработать?

braboar
() автор топика
Ответ на: комментарий от MKuznetsov

современные однопоточные приложение работают очень быстро…
Qt значит сигналы-слоты

для Qt уже однопоточное приложение - это большая проблема с отрисовкой, т.к. GUI только в этом потоке и работает, понятно что там есть костыли, но от этого они так и останутся костылями. А кроме GUI чтото делать то программе еще нужно, поэтому уже не однопоточное.

anonymous2 ★★★★★
()
Последнее исправление: anonymous2 (всего исправлений: 3)
Ответ на: комментарий от braboar

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

Создавать по сигналу сразу thread, данные все там уже должны быть. нет данных нечего и создавать. Выделенный jobs pool закончился? жди. Зачем куча потоков в wait пока не понимаю… ну может у вас такая задача.

anonymous2 ★★★★★
()
Ответ на: комментарий от anonymous2

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

rumgot ★★★★★
()
Последнее исправление: rumgot (всего исправлений: 1)
Ответ на: комментарий от braboar

Если можно, с этого места поподробней: что понимается под сигналом и как его обработать?

Поток спит на std::condition_variable::wait_for указанное время. Это типа таймера.

Если нужно выйти из ожидания досрочно, например при вызове твоего метода slot_need_stop, то вызывается std::condition_variable::notify_one (или notify_all). Поток просыпается, видит, что _running == false и выходит из цикла while (завершает работу).

ox55ff ★★★★★
()
Ответ на: комментарий от MKuznetsov

Только те которые detached (не join-able) можно окрестясь прибить через pthread_cancel

Можно любые, и вовсе не прибить. Функция pthread_cancel не прибивает поток, а лишь ставит ему запрос на выход, который можно явно проверить внутри потока при помощи pthread_testcancel, в этой точке поток и завершиться. Так же точками неявного завершения текущего потока являются вызовы pthread_join, pthread_cond_wait и pthread_cond_timedwait.

raspopov
()