how in BOOST send a signal in a thread and have the corresponding slot executed in another thread?

For an event loop use boost::asio::io_service. You can post tasks inside this object and have another thread execute them, in a thread safe way:

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

Not directly, because boost does not provide an event loop.

To have a signal handled in another thread, that another thread needs to be checking the queue of handlers it should run and execute them (which usually means some kind of event-loop). Boost does not provide one, so you'll need to get it from elsewhere or write it.

If you have an event-loop, that does not provide signals, (or implement some simple solution with queues) you should be able to (ab)use boost.signals2 (not boost.signals, because that version is not thread-safe) by overriding the operator+= to wrap each handler in something, that will queue it for execution in the other thread. You might even be able to implement it for signals with return values (which is not supported by Qt, but is supported by boost), but you'll have to be careful to avoid dead-lock.


Here's a complete example of the above mentioned io_service, executor_work_guard, signals2::signal.

  • io_service is the event loop handler
  • executor_work_guard make sure the m_service.run() doesn't only execute once
  • signal/slot decouples the sender and receiver
  • the thread runs all the process of the 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();
}

Tags:

C++

Boost

Signals