С++ 11: std:: thread внутри класса, выполняющего член функции с инициализацией потока в конструкторе
Я пытаюсь использовать std:: thread из С++ 11. Я не мог найти нигде, если возможно иметь std:: thread внутри класса, выполняющего один из его членов функции. Рассмотрим пример ниже...
В моей попытке (ниже) функция запускается().
Я компилирую с gcc-4.4 с флагом -std = С++ 0x.
#ifndef RUNNABLE_H
#define RUNNABLE_H
#include <thread>
class Runnable
{
public:
Runnable() : m_stop(false) {m_thread = std::thread(Runnable::run,this); }
virtual ~Runnable() { stop(); }
void stop() { m_stop = false; m_thread.join(); }
protected:
virtual void run() = 0;
bool m_stop;
private:
std::thread m_thread;
};
class myThread : public Runnable{
protected:
void run() { while(!m_stop){ /* do something... */ }; }
};
#endif // RUNNABLE_H
Я получаю эту ошибку и другие: (такая же ошибка с и без $this)
Runnable.h|9|error: no matching function for call to ‘std::thread::thread(<unresolved overloaded function type>, Runnable* const)’|
При передаче указателя.
Runnable.h|9|error: ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function. Say ‘&Runnable::run’|
Ответы
Ответ 1
Этот подход неверен.
Проблема заключается в том, что пока объект все еще находится в стадии разработки, его тип по-прежнему не является самым производным типом, а типом выполняемого конструктора. Это означает, что при запуске потока объект по-прежнему остается Runnable
, и вызов run()
может быть отправлен на Runnable::run()
, что является чисто виртуальным, и это, в свою очередь, вызовет поведение undefined.
Хуже того, вы можете столкнуться с ложным чувством безопасности, поскольку, возможно, в некоторых случаях поток, который запускается, может занять достаточно много времени, чтобы текущий поток завершил конструктор Runnable
и введите объект myThread
, и в этом случае новый поток выполнит правильный метод, но изменит систему, в которой вы выполняете программу (разное количество ядер или нагрузка системы или любое другое несвязанное обстоятельство), и программа будет авария в производстве.
Ответ 2
Вот код, который нужно переделать:
#ifndef RUNNABLE_H
#define RUNNABLE_H
#include <atomic>
#include <thread>
class Runnable
{
public:
Runnable() : m_stop(), m_thread() { }
virtual ~Runnable() { try { stop(); } catch(...) { /*??*/ } }
Runnable(Runnable const&) = delete;
Runnable& operator =(Runnable const&) = delete;
void stop() { m_stop = true; m_thread.join(); }
void start() { m_thread = std::thread(&Runnable::run, this); }
protected:
virtual void run() = 0;
std::atomic<bool> m_stop;
private:
std::thread m_thread;
};
class myThread : public Runnable
{
protected:
void run() { while (!m_stop) { /* do something... */ }; }
};
#endif // RUNNABLE_H
Некоторые примечания:
- Объявление
m_stop
как простого bool
, как вы, было ужасно недостаточным; читать на барьерах памяти
-
std::thread::join
может вызывать так называемое его без try..catch
от деструктора безрассудно
-
std::thread
и std::atomic<>
не скопируются, поэтому Runnable
следует пометить как таковой, если не по какой-либо другой причине, кроме как избежать C4512 предупреждений с VС++