什么是通过外部信号停止std :: thread的有效方法?

时间:2016-01-07 21:36:26

标签: c++ multithreading c++11

这是一个不按设计工作的代码请解释我这里有什么问题(简化代码以使其更易读)。

shm_server server;

std::thread s{server};

// some work...

std::cout << "press any key to stop the server";
_getch();
server.stop();
s.join();

看起来我为stop类的另一个副本调用shm_server方法。 因为stop()只将std::atomic_bool done;shm_server成员)设置为true,但我看到线程函数(这是operator()的{​​{1}})仍然看到shm_server等于done

false只移动了构造函数吗?

在这种典型情况下如何正确地向服务器发送信号?

std::thread

1 个答案:

答案 0 :(得分:2)

你可以通过shm_server复制来让自己陷入困境。我相信你这样做是为了摆脱编译器错误,而不是因为该类上的复制语义特别有用。我建议你再次删除该构造函数。如果你真的想要复制语义,请让构造函数通过引用const来获取它的参数。

class shm_server  // cannot copy or move
{

  std::atomic_bool done_ {};

public:

  void
  operator()()
  {
    this->run();
  }

  void
  run()
  {
    using namespace std::chrono_literals;
    while (!this->done_.load())
      {
        std::clog << std::this_thread::get_id() << " working..." << std::endl;
        std::this_thread::sleep_for(1ms);
      }
  }

  void
  stop()
  {
    this->done_.store(true);
  }

};

为方便起见,我已将调用运算符的逻辑分解为命名函数。您不需要这样做但我稍后会在示例中使用它。

现在我们不得不让run成员函数在自己的线程上执行。如果你写

shm_server server {};
std::thread worker {server};  // won't compile

编译器不会让你这样做 - 正如你自己猜想的那样 - 会尝试复制那些不可复制的server对象。

@Ben Voigtsuggestedserver对象包装到std::reference_wrapper中,{{3}}是一个行为类似于引用的对象。

shm_server server {};
std::thread worker {std::ref(server)};

这可以按预期工作,并有效地将指针传递给std::thread构造函数,而不是实际的server对象。

但是,我发现这不是最直接的解决方案。您真正想要做的是在另一个线程上运行server的成员函数。 std::thread的构造函数可以帮助您实现这一目标。

shm_server server {};
std::thread worker {&shm_server::run, &server};

在这里,我将run成员函数的地址和指向server对象的指针(将作为隐式this指针传递)作为参数传递。我已经介绍了run函数,因为语法可能不那么令人恼火。您也可以直接传递呼叫运营商的地址。

shm_server server {};
std::thread worker {&shm_server::operator(), &server};

这是一个完整的例子。

int
main()
{
  using namespace std::chrono_literals;
  std::clog << std::this_thread::get_id() << " starting up" << std::endl;
  shm_server server {};
  std::thread worker {&shm_server::operator(), &server};
  //std::thread worker {std::ref(server)};                 // same effect
  //std::thread worker {&shm_server::run, &server};        // same effect
  std::this_thread::sleep_for(10ms);
  server.stop();
  worker.join();
  std::clog << std::this_thread::get_id() << " good bye" << std::endl;
}

可能的输出:

140435324311360 starting up
140435306977024 working...
140435306977024 working...
140435306977024 working...
140435306977024 working...
140435306977024 working...
140435306977024 working...
140435306977024 working...
140435306977024 working...
140435306977024 working...
140435306977024 working...
140435324311360 good bye

如果你认为shm_server只有在自己的线程上运行才会有用,你也可以给它一个std::thread作为数据成员并拥有它的构造函数(或者一个专用的{{1}成员函数,如果你愿意)start和它的析构函数加入线程。

start