简体   繁体   English

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

[英]What is a valid way to stop a std::thread by an external signal?

This is a code which works not as designed please explain me what is wrong here (code simplified to make this more readable). 这是一个不按设计工作的代码请解释我这里有什么问题(简化代码使其更具可读性)。

shm_server server;

std::thread s{server};

// some work...

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

It looks like I call a stop method for another copy of shm_server class. 看起来我为另一个shm_server类副本调用了stop方法。 because stop() only sets std::atomic_bool done; 因为stop()只设置std::atomic_bool done; ( shm_server member) to true but I see that thread function (this is operator() of shm_server ) still sees done equal to false . shm_server成员)为true但我看到线程函数(这是shm_server operator() )仍然看到done等于false

std::thread has only move contructor? std::thread只移动构造函数?

How to send a signal to server correctly in this typical case? 在这种典型情况下如何正确地向服务器发送信号?

class shm_server {
    std::atomic_bool    done;
public:
    shm_server() : done{false} {}
    shm_server(shm_server& ) : done{false} {}
    void operator()() {
       while (!done) {
       //...
       }
    }
    void stop() {
       done = true;
    }
};

You have somehow shot yourself into the foot by making shm_server copyable. 通过使shm_server复制,你以某种方式射入了自己的脚。 I believe you did this in order to get rid of the compiler error, not because copy semantics on that class are particularly useful. 我相信你这样做是为了摆脱编译器错误,而不是因为该类上的复制语义特别有用。 I'd recommend you delete that constructor again. 我建议你再次删除该构造函数。 If you really want copy semantics, have the constructor take its argument by reference to const . 如果你真的想要复制语义,请让构造函数通过引用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);
  }

};

I have factored out the logic of the call operator into a named function for convenience. 为方便起见,我已将调用运算符的逻辑分解为命名函数。 You don't need to do that but I'll use it later in the example. 您不需要这样做,但我稍后会在示例中使用它。

Now we somehow have to make the run member function be executed on its own thread. 现在我们必须以某种方式使run成员函数在它自己的线程上执行。 If you write 如果你写

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

the compiler won't let you do it as it – as you have conjectured yourself – would attempt to copy the server object which isn't copyable. 编译器不会让你这样做 - 正如你自己猜测的那样 - 会尝试复制不可复制的server对象。

@Ben Voigt has suggested to wrap the server object into a std::reference_wrapper which is an object that behaves like a reference. @Ben Voigt 建议server对象包装到std::reference_wrapper ,这是一个行为类似于引用的对象。

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

This works as intended and effectively passes a pointer to the std::thread constructor, rather than the actual server object. 这按预期工作,并有效地将指针传递给std::thread构造函数,而不是实际的server对象。

However, I find that this is not the most straight-forward solution here. 但是,我发现这不是最直接的解决方案。 What you really want to do is run a member function of your server on a different thread. 您真正想要做的是在不同的线程上运行server的成员函数。 And std::thread 's constructor lets you do just that. std::thread的构造函数可以让你做到这一点。

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

Here, I'm passing the address of the run member function and a pointer to the server object (that will be passed as the implicit this pointer) as argument. 在这里,我传递的地址run成员函数的指针server对象(即会隐式传递this指针)作为参数。 I have introduced the run function since the syntax might be less irritating. 我已经介绍了run函数,因为语法可能不那么令人恼火。 You can also pass the address of the call operator directly. 您也可以直接传递呼叫运营商的地址。

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

Here is a complete example. 这是一个完整的例子。

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;
}

Possible output: 可能的输出:

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

If you believe that a shm_server will only ever be useful if run on its own thread, you can also give it a std::thread as data member and have its constructor (or a dedicated start member function, if you prefer) start and its destructor join the thread. 如果你认为shm_server只有在自己的线程上运行才会有用,你也可以给它一个std::thread作为数据成员并拥有它的构造函数(或者一个专用的start成员函数,如果你愿意的话)start和它析构函数加入线程。

#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>

class shm_server
{

  std::atomic_bool done_ {};
  std::thread worker_ {};

public:

  shm_server()
  {
    this->worker_ = std::thread {&shm_server::run_, this};
  }

  ~shm_server()
  {
    this->done_.store(true);
    if (this->worker_.joinable())
      this->worker_.join();
  }

private:

  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);
      }
  }

};


int
main()
{
  using namespace std::chrono_literals;
  std::clog << std::this_thread::get_id() << " starting up" << std::endl;
  {
    shm_server server {};
    std::this_thread::sleep_for(10ms);
  }
  std::clog << std::this_thread::get_id() << " good bye" << std::endl;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM