- 作者: 雪山肥鱼
- 时间: 20211212 14:41
- 目的: asio 中 回调 初探
boost 开发环境
Linux + CLion + CMake 即可
如果是正版的CLion, 可以支持远程调试,还是非常方便的。
简单应用
#include <iostream>
#include <boost/asio.hpp>
int main(int argc, char ** argv) {
boost::asio::io_service io;
boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));
//阻塞
t.wait();
std::cout << "Hello, World!" << std::endl;
return 0;
}
t.wait() 阻塞,当然也可以变相认证, boost库环境已经安装成功。 因为引入了Cmake, 所以我没有些makefile。
回调初探
void callback(const boost::system::error_code &) {
cout<<"hello world"<<endl;
}
void callback2(const boost::system::error_code &) {
cout <<"second call, but frist run"<<endl;
}
int main(int argc, char ** argv) {
boost::asio::io_service io;
boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));
t.async_wait(callback);
boost::asio::deadline_timer t2(io, boost::posix_time::seconds(2));
t2.async_wait(callback2);
cout<<"start run"<<endl;
io.run();
cout<<"finish run"<<endl;
return 0;
}
只有调度了 io.run 事件才会进入 io_service, 才会实现事件
上述代码行为:
2s -> 输出 secod call, but first run
3s -> 输出 hello world
总共是5s,因为异步。
- 细节:
async_wait() 需要的是一个调用对象。 - 函数 lambda 表达式
必须是
void(*)(const boost::system::error_code &)
void callback2(const boost::system::error_code &e, int a , int b) {
cout <<"second call, but frist run"<<endl;
}
// 错误
_timer.async_wait([this](const boost::system::error_code &e , int a)
// 错误
_timer.async_wait([this](const boost::system::error_code &e , int a){
//这里还是应该做保护
if(e ==error::operation_aborted) {
cout<<a<<endl;
cout<<"cancel now"<<endl;
} else {
this->print();
}
});
否则报错, async_wait 有参数检查函数
- bind / function
bind 绑普通函数
//1. 含有error_code
void print(const boost::system::error_code& , deadline_timer *t, inc * cout);
//placeholder::error() 占位了
auto func = boost::bind(print, boost::asio::placeholders::error(), t, count);
t->async_wait(func);
//2. 不含有 error_code 竟然也可以
void print(deadline_timer *t, int *count);
auto func = boost::bind(print, t, cout);
t->async_wait(func);
绑成员函数:
_timer.async_wait(boost::bind(&Printer::print, this));
我估计是 bind 返回的function 有什么操作
asio 中的 bind 与 lambda
bind
使用bind 的原因是 在 async_wait 的时候 传入更多信息
void print(const boost::system::error_code &, deadline_timer *t, int *count) {
if (*count < 5) {
cout << *count << endl;
++(*count);
//重新指定失效时间
t->expires_at(t->expires_at() + seconds(1));
auto func = boost::bind(print, boost::asio::placeholders::error(), t, count);
t->async_wait(func);
}
}
int main(int argc, char **argv) {
io_service io;
int count = 0;
deadline_timer t(io, seconds(1));
//给一个bind 本质上是为了传递更多的参数,
//asyinc_wait , wait 都只会调用用一个 function
t.async_wait(
//error 是 从 io_service 上传过来的。
boost::bind(print, boost::asio::placeholders::error(), &t, &count)
);
//io.run 没有事件后就退出了
io.run();
cout << "Final count is " << count << endl;
return 0;
}
注意 占位符的 error, 是io_service 传过来的。
io.run 在没有事件之后 就退出了。
lambda
c++14 的引入,可以在asio 中 减少bind 的数量.能让代码看上去整洁一些。
但是像上一节中所说,必须引入 system::error_code 才行呢。
//lambda 表达式版本
void print(const boost::system::error_code &e, deadline_timer *t, int *count) {
if (*count < 5) {
cout << "count: "<<*count << ", error: "<<e<<endl;
++(*count);
t->expires_at(t->expires_at() + seconds(1));
t->async_wait([t, count](const boost::system::error_code & error) {
print(error, t, count);
});
}
}
int main(int argc, char **argv) {
io_service io;
int count = 0;
deadline_timer t(io, seconds(1));
t.async_wait([&t, &count](const boost::system::error_code & error){
print(error, &t, &count);
});
//事件要坚持到 io 活着
io.run();
cout << "Final count is " << count << endl;
return 0;
}
事件一定要在io 时活着,如果在io run 的时候,没有检测error code的机制,然而此时事件又挂了的时候,会引起意想不到的问题。
引入 shared_ptr
void print(const boost::system::error_code &e, deadline_timer *t, int *count) {
if (*count < 5) {
cout << "count: "<<*count << ", error: "<<e<<endl;
++(*count);
t->expires_at(t->expires_at() + seconds(1));
t->async_wait([t, count](const boost::system::error_code & error) {
print(error, t, count);
});
}
}
void registerPrint(io_service &io, int *count) {
auto t = std::make_shared<deadline_timer>(io, seconds(1));
//这里用t.get() 是绝对有问题的.注册进去后
//auto pt = t.get();//不应该把shareptr 的裸指针传出来
//即使是事件注册进到了io, 事件进入io之前,可能已经失效了
t->async_wait([t, count](const boost::system::error_code & error){
print(error, t, count);
});
}
int main(int argc, char **argv) {
io_service io;
int count = 0;
registerPrint(io, &count);
io.run();
cout << "Final count is " << count << endl;
return 0;
}
上述注释中,提到 用 auto pt= t.get();那是肯定不行的。这取出的是 shared_ptr 中的裸指针。
- 以参数 传入 async_wait, 并没有让引用计数+1
t->async_wait([pt, count] (const auto &error) {printf(error, t, count )};);
- 函数结束后,计数 -1, 归0.
事件已经废了,io.run 跑到,即翻车
引入类
类中可以用 bind 去绑 成员函数, 当然也可以用lambda
class Printer {
public:
Printer(io_service &io):_timer(io, seconds(1)), _count(0) {
_timer.async_wait(boost::bind(&Printer::print, this));
//const auto & 是 async_wait 要的
// _timer.async_wait([this](const boost::system::error_code &){this->print();});
}
void print() {
if(_count < 5) {
cout<<_count<<endl;
++_count;
_timer.expires_at(_timer.expires_at() + seconds(1));
_timer.async_wait(boost::bind(&Printer::print, this));
// _timer.async_wait([this](const boost::system::error_code &){this->print();});
}
}
~Printer() {
cout<<"Final count is "<<_count<<endl;
}
private:
deadline_timer _timer;
int _count;
};
int main(int argc,char ** argv) {
io_service io;
Printer p(io);
/*
* 常见错误
{
//事件已经失效,io.run 去调,会出现问题
Printer p(io);
}
*/
io.run();
return 0;
}
事件失效 检查机制
事件如果失效,io.run 跑到时 注定会翻车,所以需要注入 error 机制。即回调中要检查 error code.
class Printer {
public:
// Printer(io_service &io):_timer(io, seconds(1)), _count(0) {
// _timer.async_wait([this](const boost::system::error_code &){this->print();});
// }
Printer(io_service &io):_timer(io, seconds(1)), _count(0) {
_timer.async_wait([this](const boost::system::error_code &e ){
//这里还是应该做保护
if(e ==error::operation_aborted) {
cout<<"cancel now"<<endl;
} else {
this->print();
}
});
}
void print() {
if(_count < 5) {
cout<<_count<<endl;
++_count;
_timer.expires_at(_timer.expires_at() + seconds(1));
_timer.async_wait([this](const boost::system::error_code & e){
//这里还是应该做保护
if ( e == error::operation_aborted) {
cout<<"cancel now"<<endl;
} else {
this->print();
}
});
}
}
~Printer() {
// _timer.cancel();//并无意义,因为_timer 这个成员对象在析构的时候,如果有回调事件,会自动调cancel
// 做析构的时候 已经晚了?就是这里调了析构,io 已经带着无效事件 跑起来了?
// 看cancel 的注释 像是 传给io 一个 abort 的参数,表明 已经呗放弃了,会抛出error code 来,后续的代码也说明了这一点
cout<<"Final count is "<<_count<<endl;
}
private:
deadline_timer _timer;
int _count;
};
int main(int argc,char ** argv) {
io_service io;
{
Printer p(io);
}
io.run();
return 0;
}
想了下,cancel 只是告诉 io.run 的 error_code 置为 error::operation_aborted 而已。但并没有阻止run去调度。所以要引入 error code 检查机制。即检查错误码。
网友评论