Basic skills
Timer.1 - Using a timer synchronously
执行阻塞
All programs that use asio need to have at least one I/O execution context, such as an io_context or thread_pool object. An I/O execution context provides access to I/O functionality.
boost::asio::io_context io;
Next we declare an object of type boost::asio::steady_timer. The core asio classes that provide I/O functionality (or as in this case timer functionality) always take an executor, or a reference to an execution context (such as io_context), as their first constructor argument. The second argument to the constructor sets the timer to expire 5 seconds from now.
boost::asio::steady_timer t(io,boost::asio::chrono::seconds(5));
In this simple example we perform a blocking wait on the timer. That is, the call to steady_timer::wait() will not return until the timer has expired, 5 seconds after it was created (i.e. not from when the wait starts).
A timer is always in one of two states: "expired" or "not expired". If the steady_timer::wait() function is called on an expired timer, it will return immediately.
t.wait();
full code:
#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 \n";
return 0;
}
Timer.2 - Using a timer asynchronously
一个异步阻塞(? 为什么是异步呢?)
主要改变时把上一步的wait改成
t.async_wait([](const boost::system::error_code&){
std::cout<<"hello \n";
});
官方解释:
Using asio's asynchronous functionality means supplying a completion token, which determines how the result will be delivered to a completion handler when an asynchronous operation completes
关于completion tocken 主要和asynchronous mdel 有关,官方解释:
To achieve this, an asynchronous operation must first specify a completion signature (or, possibly, signatures) that describes the arguments that will be passed to its completion handler.
Then, the operation's initiating function takes the completion signature, completion token, and its internal implementation and passes them to the async_result trait. The
async_result
trait is a customisation point that combines these to first produce a concrete completion handler, and then launch the operation.
我的理解:根据你给的函数生成真正执行的执行函数的参数,返回值,并且等真正函数执行时,提供的tocken才会执行
full code:
#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.async_wait([](const boost::system::error_code&){
std::cout<<"hello \n";
});
io.run();
return 0;
}
io.run()也就是让io_context去执行异步函数
The asio library provides a guarantee that completion handlers will only be called from threads that are currently calling boost::asio::io_context::run(). Therefore unless the boost::asio::io_context::run() function is called the completion handler for the asynchronous wait completion will never be invoked.
The boost::asio::io_context::run() function will also continue to run while there is still "work" to do. In this example, the work is the asynchronous wait on the timer, so the call will not return until the timer has expired and the completion handler has returned.
It is important to remember to give the io_context some work to do before calling boost::asio::io_context::run(). For example, if we had omitted the above call to steady_timer::async_wait(), the io_context would not have had any work to do, and consequently boost::asio::io_context::run() would have returned immediately.
Timer.3 - Binding arguments to a completion handler
full code:
#include <iostream>
#include <boost/asio.hpp>
void printh(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(printh,boost::asio::placeholders::error, t, count));
}
}
int main()
{
boost::asio::io_context io;
boost::asio::steady_timer t(io,boost::asio::chrono::seconds(1));
int count=0;
t.async_wait(std::bind(printh,boost::asio::placeholders::error,&t,&count));
io.run();
printf("final count is %d",count);
return 0;
}
解释:
expires_at:Set the timer's expiry time as an absolute time.
expiry():Get the timer's expiry time as an absolute time.
the
std::bind
function is used to associate the extra parameters with your completion handler.
其他的和上一步差不多,关键这是一个无栈异步
Timer.4 - Using a member function as a completion handler
full code
#include <iostream>
#include <boost/asio.hpp>
#include <memory>
class printer{
private:
boost::asio::steady_timer timer_;
int count_;
public:
void printh(){
if (count_<5){
std::cout<<count_<<'\n';
++count_;
timer_.expires_at(timer_.expiry()+boost::asio::chrono::seconds(1));
timer_.async_wait(std::bind(&printer::printh,this));
}
}
explicit printer(boost::asio::io_context& io): timer_(io,boost::asio::chrono::seconds(1)),count_(0){
timer_.async_wait(std::bind(&printer::printh, this));
}
~printer(){
std::cout<<"finalllll: "<<count_<<'\n';
}
};
int main()
{
boost::asio::io_context io;
printer p(io);
io.run();
return 0;
}
将上面的东西用类封装了一下,有一点不同:
1.where is void(const boost::system::error_code&)?
The
std::bind
function works just as well with class member functions as with free functions. Since all non-static class member functions have an implicitthis
parameter, we need to bindthis
to the function. As in tutorial Timer.3,std::bind
converts our completion handler (now a member function) into a function object that can be invoked as though it has the signaturevoid(const boost::system::error_code&)
.
Timer.5 - Synchronising completion handlers in multithreaded programs
在多线程共享一个对象
full code:
#include <iostream>
#include <boost/asio.hpp>
#include <thread>
#include <memory>
#include <unistd.h>
class printer{
private:
boost::asio::strand<boost::asio::io_context::executor_type> strand;
boost::asio::steady_timer timer1;
boost::asio::steady_timer timer2;
int count;
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)));
}
void print1(){
if (count<10){
std::cout<<"timer1 "<<count<<"in tid= "<<std::this_thread::get_id()<<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<<"timer2 "<<count<<"in tid= "<<std::this_thread::get_id()<<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)));
}
}
~printer()
{
std::cout << "Final count is " << count << std::endl;
}
};
int main()
{
boost::asio::io_context io;
printer p(io);
std::thread t([&]{
std::cout<<"another start running\n";
io.run();});
std::cout << "main pid = " << std::this_thread::get_id() << std::endl;
io.run();
std::cout<<"asd\n";
t.join();
return 0;
}
注意到,这里的printer
有两个steady_timer
,要实现它,需要借助boost::asio::strand<boost::asio::io_context::executor_type>
The strand class template is an executor adapter that guarantees that, for those handlers that are dispatched through it, an executing handler will be allowed to complete before the next one is started. This is guaranteed irrespective of the number of threads that are calling boost::asio::io_context::run(). Of course, the handlers may still execute concurrently with other handlers that were not dispatched through an strand, or were dispatched through a different strand object.
同样的,complication_tocken需要boost::asio::bind_executor来辅助创建
When initiating the asynchronous operations, each completion handler is "bound" to an boost::asio::strand<boost::asio::io_context::executor_type> object. The boost::asio::bind_executor() function returns a new handler that automatically dispatches its contained handler through the strand object. By binding the handlers to the same strand, we are ensuring that they cannot execute concurrently.
在这个例子中,线程在t创建时就已经启动,t.join()函数起到线程同步的作用,main线程和t线程共享count数值,运行结果为:
main pid = another start running
1
timer2 0in tid= 1
timer1 1in tid= 3
timer2 2in tid= 3
timer1 3in tid= 1
timer2 4in tid= 1
timer1 5in tid= 3
timer2 6in tid= 3
timer1 7in tid= 1
timer2 8in tid= 1
timer1 9in tid= 3
asd
Final count is 10
本文参考:file:///D:/platform/boost_1_84_0/doc/html/boost_asio/tutorial.html
评论区