std :: thread:如何在类体中声明一个线程作为普通成员?

时间:2012-02-13 19:24:32

标签: c++ multithreading c++11

我希望为Page对象的每个实例都有一个线程。一次只能执行其中一个(只是检查指向当前正在运行的线程的指针是否可连接..)

class Page : public std::vector<Step>
{
    // ....
    void play();
    void start(); // check if no other thread is running. if there is a running thread, return. else join starter
    std::thread starter; // std::thread running this->play()
    static std::thread* current; // pointer to current running thread
    // ...
};

我希望能够启动starterPage个对象的线程。例如:

Page x , y , z;
// do some stuff for initialize pages..
x.start();
// do some other work
y.start(); // if x is finished, start y otherwise do nothing
// do some other lengthy work
z.start(); // if x and y are not running, start z

我无法将started声明为Page的成员。我发现这是因为std::thread只能在声明时初始化。 (或类似的东西,导致无法复制线程)

void x()
{
}
//...
std::thread t(x);          // this is ok
std::thread r;             // this is wrong, but I need this !
r = std::thread(this->y);  // no hope
r = std::thread(y);        // this is wrong too

2 个答案:

答案 0 :(得分:3)

您可以使用成员初始化列表将线程初始化为要运行的函数。例如,考虑Page的构造函数:

class Page {
public:
    Page(); // For example

private:
    std::thread toRun;
};

Page::Page() : toRun(/* function to run */) {
    /* ... */
}

注意我们如何使用Page构造函数中的初始化列表将toRun初始化为应该运行的函数。这样,toRun被初始化,就像您已将其声明为局部变量

一样
std::string toRun(/* function to run */);

那就是说,我认为你必须在代码中解决两个主要问题。首先,您应该继承std::vector或任何标准集合类。这些类的析构函数没有标记为virtual,这意味着如果您尝试将Page视为std::vector,则可以轻松调用未定义的行为。相反,请考虑让Pagestd::vector作为直接子对象。此外,您不应公开该类的std::thread成员。作为一般规则,数据成员应该private来增加封装,以便将来更容易修改类,并防止人们破坏所有类的不变量。

希望这有帮助!

答案 1 :(得分:2)

永远不要公开继承std容器,除非代码是丢弃代码。老实说,当推动代码时,抛弃代码的频率会变成生产代码。

我知道您不想重现整个std::vector界面。这是繁琐的写作,难以维护,并且诚实地可以制造错误。

试试这个

class Page: private std::vector
{
public:
  using std::vector::push_back;
  using std::vector::size;
  // ...
};

忽略std::vector问题,这应该适用于问题的并发部分。

class Page
{
  ~Page( void )
  {
    m_thread.join();
  }

  void start( void );

private:

  // note this is private, it must be to maintain the s_running invariant
  void play( void )
  {
    assert( s_current == this );

    // Only one Page at a time will execute this code.

    std::lock_guard<std::mutex> _{ s_mutex };
    s_running = nullptr;
  }

  std::thread m_thread;

  static Page* s_running;
  static std::mutex s_mutex;
};

Page* Page::s_running = nullptr;
std::mutex Page::s_mutex;
std::condition Page::s_condition;

void Page::start( void )
{
  std::lock_guard<std::mutex> _{ s_mutex };

  if( s_running == nullptr )
  {
    s_running = this;
    m_thread = std::thread{ [this](){ this->play(); } };
  }
}

如果在Page之前main()实例化,则此解决方案可能会出现初始化顺序问题