Boost::Asio
同步使用计时器
- 所有使用asio的程序都需要至少有一个I/O执行上下文,例如
io_context
或thread_pool
对象。I/O执行上下文提供对I/O功能的访问。
#include <iostream>
#include <boost/asio.hpp>
int main()
{
boost::asio::io_context io;
boost::asio::steady_timer t(io, boost::asio::chrono::seconds(5));
t.wait();
std::cout << "Hello, world!" << std::endl;
return 0;
}
boost::asio::steady_timer::wait()
在timer
过期后返回,也就是在timer
创建5s
后,而不是在wait()
调用5s
后。timer
只有两种状态,过期和未过期。如果wait()
是一个过期的timer
调用,那么会立即返回。
异步使用计时器
timer
不实用阻塞wait()
,调用steady_timer::async_wait()
实现异步,被调用函数签名void(const boost::system::error_code&)
,最后必须调用boost::asio::io_context::run()
成员函数。
#include <iostream>
#include <boost/asio.hpp>
void print(const boost::system::error_code& /*e*/)
{
std::cout << "Hello, world!" << std::endl;
}
int main()
{
boost::asio::io_context io;
boost::asio::steady_timer t(io, boost::asio::chrono::seconds(5));
t.async_wait(&print);
io.run();
return 0;
}
boost::asio::io_context
没有任务之后才会停止,io_context
绑定了timer
,也就是timer
过期以及print
函数返回前,会一直执行。
绑定参数
- 实现一个重复的计时器,还需要传递两个参数到处理函数,一个是
timer
的指针,还有一个是停止条件参数。std::bind()
函数可以绑定参数并将函数正确转化为void(const boost::system::error_code&)
签名的函数,需要确认参数列表对应。
#include <functional>
#include <iostream>
#include <boost/asio.hpp>
void print(const boost::system::error_code& /*e*/,
boost::asio::steady_timer* t, int* count)
{
if (*count < 5)
{
std::cout << *count << std::endl;
++(*count);
t->expires_at(t->expiry() + boost::asio::chrono::seconds(1));
t->async_wait(std::bind(print,
boost::asio::placeholders::error, t, count));
}
}
int main()
{
boost::asio::io_context io;
int count = 0;
boost::asio::steady_timer t(io, boost::asio::chrono::seconds(1));
t.async_wait(std::bind(print,
boost::asio::placeholders::error, &t, &count));
io.run();
std::cout << "Final count is " << count << std::endl;
return 0;
}
将成员函数用作处理函数
- 创建一个类,这个类有一个
boost::asio::io_context
对象引用,当初始化timer
时使用。非静态函数有一个隐性指针this,需要绑定到成员函数,std::bind()
函数将成员函数转换为一个可以被调用的函数对象,函数签名void(const boost::system::error_code&)
。
#include <functional>
#include <iostream>
#include <boost/asio.hpp>
class printer
{
public:
printer(boost::asio::io_context& io)
: timer_(io, boost::asio::chrono::seconds(1)),
count_(0)
{
timer_.async_wait(std::bind(&printer::print, this));
}
~printer()
{
std::cout << "Final count is " << count_ << std::endl;
}
void print()
{
if (count_ < 5)
{
std::cout << count_ << std::endl;
++count_;
timer_.expires_at(timer_.expiry() + boost::asio::chrono::seconds(1));
timer_.async_wait(std::bind(&printer::print, this));
}
}
private:
boost::asio::steady_timer timer_;
int count_;
};
int main()
{
boost::asio::io_context io;
printer p(io);
io.run();
return 0;
}
多线程程序中同步
- 使用strand类模版,除了初始化boost::asio::steady_timer成员,也要初始化strand成员,
boost::asio::strand<boost::asio::io_context::executor_type>
对象,strand保证它分配的执行器,一个正在执行的可以在下一个处理之前完成,而不用管调用boost::asio::io_context的线程数量。将程序绑定到同一个strand,可以保证程序不并发执行。
#include <functional>
#include <iostream>
#include <thread>
#include <boost/asio.hpp>
class printer
{
public:
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)
{
timer1_.async_wait(boost::asio::bind_executor(strand_,
std::bind(&printer::print1, this)));
timer2_.async_wait(boost::asio::bind_executor(strand_,
std::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_,
std::bind(&printer::print1, 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_,
std::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);
std::thread t([&]{ io.run(); });
io.run();
t.join();
return 0;
}
可能的输出:
Timer 2: 0
Timer 1: 1
Timer 2: 2
Timer 1: 3
Timer 2: 4
Timer 1: 5
Timer 2: 6
Timer 1: 7
Timer 2: 8
Timer 1: 9