[英]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.