BOOST 如何在一个线程中发送信号并在另一个线程中执行相应的插槽?

Posted

技术标签:

【中文标题】BOOST 如何在一个线程中发送信号并在另一个线程中执行相应的插槽?【英文标题】:how in BOOST send a signal in a thread and have the corresponding slot executed in another thread? 【发布时间】:2011-02-19 11:45:44 【问题描述】:

例如,在 Qt 中,如果您在 GUI 线程以外的线程中发出信号,该信号将在 GUI 线程中排队并稍后执行,有没有办法通过 boost 来做到这一点?

谢谢

【问题讨论】:

【参考方案1】:

对于事件循环,使用 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::signal 已被弃用,我必须使用 boost::signals2::signal.【参考方案2】:

不是直接的,因为 boost 不提供事件循环。

要在另一个线程中处理信号,另一个线程需要检查它应该运行并执行它们的处理程序队列(这通常意味着某种事件循环)。 Boost 没有提供,因此您需要从其他地方获取或编写它。

如果您有一个不提供信号的事件循环(或使用队列实现一些简单的解决方案),您应该能够(ab)使用 boost.signals2(不是 boost.signals,因为该版本不是线程-safe) 通过覆盖operator+= 将每个处理程序包装在某些东西中,这会将其排队等待在另一个线程中执行。您甚至可以为具有返回值的信号实现它(Qt 不支持,但 boost 支持),但您必须小心避免死锁。

【讨论】:

【参考方案3】:

这是上面提到的io_serviceexecutor_work_guardsignals2::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();

【讨论】:

【参考方案4】:

Chila 的回答是正确的,但它遗漏了一件重要的事情: boost::thread 对象只会调用它传递的函数一次。由于boost::io_service 在发出信号之前没有任何工作要做,因此线程将立即结束。为了解决这个问题,有一个boost::asio::io_service::work 类。 在调用io_servicerun() 方法之前,您应该创建一个工作对象并将其传递给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();

注意:在撰写本文时(提升 1.67),此方法已被弃用,您应该使用 io_context::executor_work_guard(与 io_service::work 基本相同的功能)。虽然我在使用新方法时无法编译,但工作解决方案仍在 boost 1.67 中工作。

【讨论】:

【参考方案5】:

由于某种原因,boost::asio::executor_work_guard&lt;boost::asio::io_context::executor_type&gt; 的赋值运算符被删除了,但你仍然可以构造它。

这是我发布一些可移动的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 以及线程和内存清理器进行了测试。

【讨论】:

以上是关于BOOST 如何在一个线程中发送信号并在另一个线程中执行相应的插槽?的主要内容,如果未能解决你的问题,请参考以下文章

在 C++11 中实现 boost::barrier

如何从另一个线程中断 websocket(使用 boost beast)?

如何从 Django 迁移中发送信号?

有一个返回 int 的函数,我如何使用 boost 在单独的线程中运行它?

Objective-C:在一个线程中分配并在另一个线程中释放

在一个线程中修改成员变量并在另一个线程中读取