Вот краткий пример:
class worker
{
std::thread thread1;
std::thread thread2;
worker(){}
start()
{
thread1 = std::thread([]()
{
std::this_thread::sleep_for(std::chrono::milliseconds(50000));
});
thread1.deteach();
thread2 = std::thread([]()
{
std::this_thread::sleep_for(std::chrono::milliseconds(50000));
});
thread2.deteach();
}
~worker()
{
// Ignore this part! - as per Richard's comment :)
//thread1.join(); // <-- do I need this at all?
//thread2.join(); // <-- do I need this at all?
}
}
int main()
{
worker *p_worker = new worker();
p_worker->start();
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // 1 sec
delete p_worker;
}
Я посмотрел на этот вопрос: как-сделать-я-завершить-поток-в-c11, который предполагает, что нет переносимого способа c11 для завершения потоков.
Вопросов:
EDIT - Richard pointed out this is N/A@RichardCritten ах, верно - я обновлю Q с учетом этого, спасибо :)
Обычно, если класс «владеет» потоками, вы не отключаете их, сигнализируйте им каким-то образом о завершении в деструкторе и присоединении к ним.
«Разумный ли это подход» к чему? Ваши потоки в данный момент просто спят, так что это нормально; но когда вы начинаете что-то делать с ними, если потоки используют какие-либо глобальные ресурсы, это, вероятно, не в порядке. Если ваши потоки делают что-то интенсивное для ЦП; тогда это, вероятно, тоже не нормально, так как вы хотите, чтобы они остановились, а не работали в течение 50 секунд над чем-то, что нельзя использовать.
@MatteoItalia мм .. хороший момент - я был убежден, что мне нужно отсоединить их, чтобы позволить main продолжить работу. Но я думаю, что меня это смутило, что я не присоединился к ним, спасибо :)
@UKMonkey Я думаю, ваш комментарий отвечает на то, что мне нужно знать. В моем реальном коде я использовал мьютекс, чтобы «сигнализировать», когда поток простаивает, поэтому я был бы счастлив просто уничтожить worker ...
@RichardCritten, пожалуйста, добавьте в качестве частичного ответа - я помечу его.
@MatteoItalia, пожалуйста, добавьте как частичный ответ - я размечу.
@UKMonkey, пожалуйста, добавьте как частичный ответ - я размечу.





Да, потоки полностью уничтожаются dtor std::thread, если они все еще могут быть присоединены (запущены и не отсоединены).
Однако это не хорошие новости, так как будет называться std::terminate(), убивающий весь процесс.
В общем, простое завершение имеет смысл только во избежание дальнейшего ущерба из-за неожиданного состояния или если приложение было создано для безопасного завершения именно в этот момент.
ох ... ублюдок! - поэтому, если вызывается std :: terminate (), насколько я понимаю (по общему признанию, не так уж и много!), тогда все приложение будет завершено ... как вы говорите, не очень хорошие новости :(
Все это верная информация, но, вероятно, ее следует немного уточнить для OP; бит «если они все еще могут быть присоединены» означает «если они не отсоединены и все еще работают». Итак, все это означает, что * если вы не присоединитесь к неотключенному потоку, ваша программа будет убита »; правильный курс действий здесь - присоединиться до того, как std::thread будет уничтожен.
@MatteoItalia Итак, если потоки объединены в деструкторе worker (как у меня изначально, но без отсоединения) - тогда этот подход может сработать? - т.е. std :: terminate () не будет вызываться?
Да. Отсоединение предназначено для запуска независимых потоков, которые могут запускаться сами по себе (и, возможно, присоединяться к ним другими способами). Если у вас есть владелец потока, обычно вы хотите присоединиться к нему после его удаления.
Как уже было сказано в комментариях, вы не можете присоединиться к отдельной цепочке. Отдельные потоки предназначены для независимого запуска. В общем, отсоединять поток, принадлежащий классу, - плохая идея.
Я бы предложил использовать логическое значение для управления жизненным циклом вашего потока.
Например, вы можете сделать что-то вроде этого:
class worker
{
private:
std::thread thread1;
std::atomic<bool> thread1ShouldRun;
std::thread thread2;
std::atomic<bool> thread2ShouldRun;
void workerFunc1() {
bool threadWorkIsDone = false;
while (thread1ShouldRun.load()) {
// Do Stuff
// Set threadXShouldRun to false when you're done
// thread1ShouldRun.store(false);
}
}
void workerFunc2() {
bool threadWorkIsDone = false;
while (thread2ShouldRun.load()) {
// Do Stuff
// Set threadXShouldRun to false when you're done
// thread2ShouldRun.store(false);
}
}
public:
worker() {}
void start()
{
thread1ShouldRun.store(true);
thread1 = std::thread(&worker::workerFunc1, this);
thread2ShouldRun.store(true);
thread2 = std::thread(&worker::workerFunc2, this);
}
~worker()
{
thread1ShouldRun.store(false);
// Protection in case you create a worker that you delete and never call start()
if (thread1.joinable())
thread1.join();
thread2ShouldRun.store(false);
if (thread2.joinable())
thread2.join();
}
};
int main()
{
worker *p_worker = new worker();
p_worker->start();
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // 1 sec
delete p_worker; // Threads will be joined here
}
Мне нравится использование атома для этого :)
Вы не можете присоединиться к отдельной цепочке