自己翻译的 没有任何参考价值
Timer.5 - Synchronising handlers in multithreaded programs
https://www.boost.org/doc/libs/1_74_0/doc/html/boost_asio/tutorial/tuttimer5.html
Timer.5 - Synchronising handlers in multithreaded programs
这次的教程会演示在一个多线程程序中用strand类模板来同步执行回调函数.
前面的四篇教程中,我们通过使用单线程上的io_context::run()函数,避免了多线程同步的问题.如你所知,asio库保证回调函数都只会在io_context::run()的执行线程上被执行.也就是说,如果我们只在一个线程上执行io_context::run(),那就意味着各个回调函数不可能并行执行.
以单线程方式来入门asio编程是一个不错的选择.但是单线程也有它的的缺点: 它对程序,特别是服务器上的程序存在限制,这些限制包括:
- 存在耗时操作时.系统的实时响应能力差.
- 在多处理器系统上不能扩展.
如果你苦于这些限制的话,通常的解决方式是去搞一池子线程来执行io_context::run().这样就能实现回调函数的并行处理了.然而,这也使得它们能够同时访问线程不安全的共享数据.因此我们需要一种手段来进行同步.
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>
#include <boost/bind/bind.hpp>
首先跟之前一样,定义一个printer类.不过这次在原先的基础改成了两个并行的计时器.
class printer
{
public:
构造函数里除了初始化两个boost::asio::steady_timer成员之外,还初始化了一个叫strand_的成员,这是一个boost::asio::strand< boost::asio::io_context::executor_type >类型的对象.
strand类模板是一种executor adapter(可能翻译成执行者适配器?),它能够保证由他经手的回调函数不会同时执行.当然了,不由同一个strand调度的各个回调函数还是可以并行执行的.
printer(boost::asio::io_context& io)
: strand_(boost::asio::make_strand(io)),
timer1_(io, boost::asio::chrono::seconds(1)),
timer2_(io, boost::asio::chrono::seconds(1)),
count_(0)
{
初始化这些异步操作的时候,每一个回调函数都绑到了一个boost::asio::strand< boost::asio::io_context::executor_type >的类对象上.函数boost::asio::bind_executor()返回的是一个新的回调函数, 这个回调函数已经自动由strand负责调度了.这么一来,这两个函数绑到了同一个strand上面,我们就能确保他们不会被同时执行了.
timer1_.async_wait(boost::asio::bind_executor(strand_,
boost::bind(&printer::print1, this)));
timer2_.async_wait(boost::asio::bind_executor(strand_,
boost::bind(&printer::print2, this)));
}
~printer()
{
std::cout << "Final count is " << count_ << std::endl;
}
在一个多线程程序中,如果回调处理操作访问了共享资源,那他们的异步操作应该是受同步的.本教程中,print1跟print2访问的共享资源是std::cout和成员变量count_.
void print1()
{
if (count_ < 10)
{
std::cout << "Timer 1: " << count_ << std::endl;
++count_;
timer1_.expires_at(timer1.expiry() +
boost::asio::chrono::seconds(1));
timer1_.async_wait(boost::asio::bind_executor(strand_,
boost::bind(&printer1, this)));
}
}
void print2()
{
if (count_ < 10)
{
std::cout << "Timer 2: " << count_ << std::endl;
++count_;
timer2_.expires_at(timer2_.expiry() +
boost::asio::chrono::seconds(1));
timer2_.async_wait(boost::asio::bind_executor(strand_,
boost::bind(&printer::print2, this)));
}
}
private:
boost::asio::strand<boost::asio::io_context::executor_type> strand_;
boost::asio::steady_timer timer1_;
boost::asio::steady_timer timer2_;
int count_;
};
这次的main函数要在两个线程分别调用io_context::run(): 主线程调用一次,再申请一个线程再调一个.这将用boost::thread类对象来完成.
和之前单线程的时候一样,多线程上的io_context::run()也会等手头"活儿"干完之后再停止.后台线程在所有异步操作执行结束之前不会退出.
int main()
{
boost::asio::io_context io;
printer p(io);
boost::thread t(boost::bind(&boost::asio::io_context::run, &io));
io.run();
t.join();
return 0;
}
再回顾一下整体的代码(发现好像stl里没有bind_executor的替代品)
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>
#include <boost/bind/bind.hpp>
class printer
{
public:
printer(boost::asio::io_context& io)
: strand_(boost::asio::make_strand(io)),
timer1_(io, boost::asio::chrono::seconds(1)),
count_(0)
{
timer1_.async_wait(boost::asio::bind_executor(strand_,
boost::bind(&printer::print1, this)));
timer2_.async_wait(boost::asio::bind_executor(strand_,
boost::bind(&printer::print2, this)));
}
~printer()
{
std::cout << "Final count is " << count_ << std::endl;
}
void print1()
{
if (count_ < 10)
{
std::cout << "Timer 1: " << count_ << std::endl;
++count_;
timer1_.expires_at(timer1_.expiry() +
boost::asio:;chrono::seconds(1));
timer1_.async_wait(boost::asio::bind_executor(strand_,
boost::bind(&printer::printer1, this)));
}
}
void print2()
{
if (count_ < 10)
{
std::cout << "Timer 2: " << count_ << std::endl;
++count_;
timer2_.expires_at(timer2_.expiry() +
boost::asio::chrono::seconds(1));
timer2_.async_wait(boost::asio::bind_executor(strand_,
boost::bind(&printer::print2, this)));
}
}
private:
boost::asio::strand<boost::asio::io_context::executor_type> strand_;
boost::asio::steady_timer timer1;
boost::asio::steady_timer timer2;
int count_;
};
int main()
{
boost::asio::io_context io;
printer p(io);
boost::thread t(boost::bind(&boost::asio::io_context::run, &io));
io.run();
t.join();
return 0;
}
Timer.5 - Synchronising handlers in multithreaded programs
https://www.boost.org/doc/libs/1_74_0/doc/html/boost_asio/tutorial/tuttimer5.html