как в BOOST отправить сигнал в потоке и выполнить соответствующий слот в другом потоке? - PullRequest
10 голосов
/ 19 февраля 2011

Например, в Qt, если вы генерируете сигнал в потоке, отличном от потока GUI, сигнал ставится в очередь и выполняется позже в потоке GUI, есть ли способ сделать это с boost?

спасибо

Ответы [ 5 ]

17 голосов
/ 24 февраля 2011

Для цикла событий используйте 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(); 
2 голосов
/ 19 февраля 2011

Не напрямую, поскольку boost не обеспечивает цикл обработки событий.

Чтобы обработать сигнал в другом потоке, другой поток должен проверять очередь обработчиков, которые он должен запустить и выполнить (которые обычноозначает какой-то цикл обработки событий).Boost не обеспечивает его, поэтому вам нужно получить его из другого места или написать его.

Если у вас есть цикл обработки событий, который не дает сигналов (или реализуете какое-то простое решение с очередями), выдолжен иметь возможность (ab) использовать boost.signals2 (не boost.signals, потому что эта версия не является поточно-ориентированной), переопределив operator+=, чтобы обернуть каждый обработчик во что-то, что поставит его в очередь для выполнения в другом потоке.Возможно, вы даже сможете реализовать его для сигналов с возвращаемыми значениями (что не поддерживается Qt, но поддерживается boost), но вам следует быть осторожным, чтобы избежать тупиковой блокировки.

1 голос
/ 18 июня 2019

Вот полный пример вышеупомянутых 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();
}
1 голос
/ 02 июля 2018

Ответ Чилы верен, но в нем отсутствует одна важная вещь: объект boost::thread будет вызывать функцию, переданную ему только один раз.Поскольку boost::io_service не нужно выполнять до тех пор, пока не будет издан сигнал, поток завершится немедленно.Чтобы противостоять этому есть класс boost::asio::io_service::work.Перед вызовом run() метода io_service необходимо создать рабочий объект и передать ему 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.

0 голосов
/ 30 августа 2018

По какой-то причине оператор присваивания 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 с дезинфицирующими средствами для потоков и памяти.

...