美文网首页
boost::asio 回调初探

boost::asio 回调初探

作者: 404Not_Found | 来源:发表于2021-12-11 15:58 被阅读0次
  • 作者: 雪山肥鱼
  • 时间: 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 中的裸指针。

  1. 以参数 传入 async_wait, 并没有让引用计数+1
t->async_wait([pt, count] (const auto &error) {printf(error, t, count )};);
  1. 函数结束后,计数 -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 检查机制。即检查错误码。

相关文章

网友评论

      本文标题:boost::asio 回调初探

      本文链接:https://www.haomeiwen.com/subject/hhprfrtx.html