[英]how in BOOST send a signal in a thread and have the corresponding slot executed in another thread?
例如,如果你在GUI线程之外的线程中发出信号,信号被排队并在GUI线程中稍后执行,那么有没有办法用boost来做到这一点?
谢谢
对于事件循环,请使用boost :: asio :: io_service。 您可以在线程安全的方式中在此对象中发布任务并让另一个线程执行它们:
struct MyClass
{
boost::io_service service;
void doSomethingOp() const { ... }
void doSomething()
{
service.post(boost::bind(&MyClass::doSomethingOp, this));
}
void loop()
{
service.run(); // processes the tasks
}
};
boost::signal<void()> mySignal;
MyClass myClass;
mySignal.connect(boost::bind(&MyClass::doSomething, boost::ref(myClass)));
// launches a thread and executes myClass.loop() there
boost::thread t(boost::bind(&MyClass::loop(), boost::ref(myClass)));
// calls myClass.doSomething() in this thread, but loop() executes it in the other
mySignal();
不是直接的,因为boost不提供事件循环。
要在另一个线程中处理信号,另一个线程需要检查处理程序队列,它应该运行并执行它们(这通常意味着某种事件循环)。 Boost不提供一个,所以你需要从别处获取它或写它。
如果你有一个不提供信号的事件循环(或者用队列实现一些简单的解决方案)你应该能够(ab)使用boost.signals2(而不是boost.signals,因为那个版本不是线程安全的)通过重写operator+=
来包装每个处理程序,将其排队以便在另一个线程中执行。 您甚至可以为具有返回值的信号实现它(Qt不支持,但受boost支持),但您必须小心避免死锁。
Chila的答案是正确的,但它缺少一件重要的事情: boost::thread
对象只会调用它传递过一次的函数。 由于boost::io_service
在发出信号之前无需做任何工作,因此线程将立即完成。 为了解决这个问题,有一个boost::asio::io_service::work
class。 在调用io_service
的run()
方法之前,您应该创建一个工作对象并将其传递给io_service
:
//as a class variable
std::shared_ptr<boost::asio::io_service::work> worker;
//before you call run() of the io_service yourIOService
worker = std::make_shared<boost::asio::io_service::work>(yourIOService);
//If you want the service to stop
worker.reset();
注意:在编写本文时(boost 1.67),此方法已被弃用,您应该使用io_context::executor_work_guard
(与io_service::work
基本相同的功能)。 虽然我在使用新方法时无法编译,但工作解决方案仍在使用boost 1.67。
以下是上述io_service
, executor_work_guard
, signals2::signal
的完整示例。
io_service
是事件循环处理程序 executor_work_guard
确保m_service.run()不仅执行一次 signal
/ slot
将发送器和接收器分离 thread
运行io_service
所有进程 #include <boost/thread.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/executor_work_guard.hpp>
#include <boost/signals2/signal.hpp>
class IOService
{
public:
IOService() : m_worker(boost::asio::make_work_guard(m_service)) {}
~IOService() {}
// slot to receive signal
void slotMessage(std::string msg)
{
m_service.post(boost::bind(&IOService::process, this, msg));
}
// start/close background thread
bool start()
{
if (m_started)
return true;
m_started = true;
// start reader thread
m_thread = boost::thread(boost::bind(&IOService::loop, this));
return m_started;
}
void loop()
{
m_service.run();
}
void close()
{
m_worker.reset();
if (m_thread.joinable())
m_thread.join();
m_started = false;
}
// process
void process(std::string msg)
{
printf("process %s\n", msg.c_str());
}
private:
bool m_started = false;
boost::asio::io_service m_service;
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> m_worker;
boost::thread m_thread;
};
int main()
{
// service instance
IOService serv;
serv.start();
// signal to slot
boost::signals2::signal<void(std::string)> signalMessage;
signalMessage.connect(boost::bind(&IOService::slotMessage, boost::ref(serv), _1));
// send one signal
signalMessage("abc");
// wait and quit
boost::this_thread::sleep(boost::chrono::seconds(2));
serv.close();
}
出于某种原因, boost::asio::executor_work_guard<boost::asio::io_context::executor_type>
的赋值运算符被删除,但您仍然可以构造它。
这是我的代码版本,它发布一些可移动的Event
对象并在运行io_context::run()
的线程上处理它:
class MyClass {
public:
MyClass () : m_work(boost::asio::make_work_guard(m_service)) {}
size_t loop() {
return m_service.run();
}
void stop() {
m_work.reset();
}
void doSomething(Event&& e) {
m_service.post([this, e=std::move(e)]{ doSomethingOp(std::move(e)); });
}
private:
void doSomethingOp(const Event& event) {
...
}
//data:
boost::asio::io_context m_service;
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> m_work;
};
它需要C ++ 14,并使用VS2017和GCC 6.4进行线程和内存消毒剂测试。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.