В Qt, например, если вы излучаете сигнал в потоке, другом, который является потоком GUI, сигнал завершается и выполняется позже в потоке GUI, есть ли способ сделать это с boost?
спасибо
В Qt, например, если вы излучаете сигнал в потоке, другом, который является потоком 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, потому что эта версия не является нитью) -safe), переопределяя operator+=
, чтобы обернуть каждый обработчик чем-то, который будет помещать его в очередь для выполнения в другом потоке. Возможно, вы даже сможете реализовать его для сигналов с возвращаемыми значениями (которые не поддерживаются Qt, но поддерживаются boost), но вы должны быть осторожны, чтобы избежать блокировки.
Ответ Chila правильный, но в нем отсутствует одна важная вещь: объект 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.
По какой-то причине оператор присваивания 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;
};
Он требует С++ 14 и был протестирован с VS2017 и GCC 6.4 с обработчиками потоков и памяти.
Вот полный пример вышеупомянутого 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();
}