C++Asio库学习 Timer.1~3

asio c++ 库

MacOS 安装

直接解压源码,例如从官网https://sourceforge.net/projects/asio/files/asio/1.28.0%20%28Stable%29/ 下载。

之后,使用如下命令编译程序,其中path-to-asio为asio文件所在路径
clang++ example.cpp -std=c++20 -stdlib=libc++ -I path-to-asio/include/

Timer.1 同步timer

这个示例程序通过展示如何在计时器上执行阻塞等待,来引入asio库。我们从包含必要的头文件开始。所有asio类可以通过仅仅包含"asio.hpp"头文件来使用。

#include <iostream>
#include <asio.hpp>

所有使用asio的程序需要至少一个IO执行环境,例如一个io_context或者thread_pool对象。一个IO执行环境提供IO功能的访问。我们在主函数中声明一个类型为io_context的对象。

int main{
    asio::io_context io;

接下里啊我们声明类型为asio::steady_timer的对象。这个提供IO功能的核心asio对象总是将一个执行器或者一个对于执行环境(例如io_context)的引用作为它们的构造函数的第一个参数。构造函数的第二个参数将计时器设置为计时5s。

asio::steady_timer t(io, asio::chrono::seconds(5));

在这个简单的例子中,我们在timer上执行阻塞等待。也就是说,steady_timer::wait()的调用直到计时器超时才会返回,在它创建后5s(也就是说,不是从wait调用开始的时候)。

一个timer总是两个状态之一:“expired” / “not expired”。如果steady_timer::wait()函数在一个expired timer上调用,它将会立刻返回。

t.wait()

最后,我们打印通常的“Hello World”消息来展示何时timer expire。

std::cout << "Hello World!" << std::endl;
return 0;
}

Timer.2 使用异步的timer

这个示例程序展示了如何使用asio的异步功能,通过更改示例timer.1在timer上执行异步等待。

#include <iostream>
#include <asio.hpp>

使用 asio的异步功能意味着需要提供一个completion的标记,这可以决定结果如何分发给completion handler当异步操作完成时。在这个程序中,我们定义了一个名为print的函数,它在异步等待完成时调用。

void print(const asio::error_code& /* e */){
    std::cout << "Hello, world!" << std::endl;
}
int main(){
    asio::io_context io;
    asio::steady_timer t(io, asio::chrono::seconds(5));

接下来,不是像timer.1执行阻塞等待,我们调用steady_timer::async_wait()函数来执行异步等待。当调用这个函数时,我们传入在上面定义的print函数。

t.async_wait(&print);

最后,我们必须在io_context对象上调用io_context::run()成员函数。asio库提供了一种保证,completion handlers只会从当前调用io_context::run的线程上调用。因此如果io_context::run函数不被调用,那么这个completion handler也将不会调用。

当仍有工作需要做时,这个io_context::run函数也将会继续运行。在这个例子中,这个工作是在timer上的异步等待,所以这个调用直到这个timer过期并且这个completion handler返回后,才会返回。

需要记住一件重要的事,在调用io_context::run函数前给io_context一些工作来做。例如,如果我们忽略上方对于steady_timer::async_wait()的调用,这个io_context将没有工作需要做,因此io_context::run将会立刻返回。

io::run();
return 0;

Timer.3 给completion handler绑定参数

在这个示例中,我们将更改Timer.2来使得timer每秒可以发射一次。将展示如何传入额外的参数向你的handler函数中。

#include <iostream>
#include <asio.hpp>
#include <boost/bind/bind.hpp>

为了使用asio实现重复的timer,你需要更改timer的过期时间在你的completion handler中,并且开始一个新的异步等待。显然这意味着completion handler将需要能够访问timer对象。在这里,我们向print函数中添加两个新的参数:

  • 一个指向timer对象的指针
  • 一个计数器,使得当计数器发射六次时可以停止程序
void print(const asio::error_code& /*e*/,asio::steady_timer* t, int* count){

正如上面所提及的,这个示例程序使用一个计数器来停止运行,当计时器发射第六次时。然而你将观察到没有显式调用来要求io_context终止。回忆一下,在timer.2中我们了解到io_context::run函数会在没有工作要做时完成。通过当计数器到达5时。不开启一个在timer上新的异步等待,这个io_context将会停止运行。

if(*count < 5){
    std::cout << *count << std::endl;
    ++(*count);

接下来我们从timer的expiry time移动1s。

t->expires_at(t->expiry() + asio::chrono::seconds(1));

之后,我们在timer上开始一个新的异步等待。正如你所见,bind函数被用来将额外的参数于你的completion handler关联。这个steady_timer::async_wait()函数需要一个使用签名void(const asio::error_code&)的handler函数。绑定额外参数将你的print函数转化为一个正确的函数对象。

在这个例子中,这个asio::placeholders::error参数是一个对于错误对象的命名占位符。当执行异步操作时,如果使用bind,你必须确定只要匹配到handler参数列表的参数。在Timer.4中,你将会看到这个占位符可以被消除,如果这个参数对于completion handler不是必须的。

t->async_wait(bind(print,asio::placeholders::error,t,count));
}
}
int main(){
    asio::io_context io;

一个新的计数器变量需要增加

int count = 0;
asio::steady_timer t(io, asio::chrono::seconds(1));
t.async_wait(bind(print,asio::placeholders::error, &t, &count));
io::run();
std::cout << "Final count is " << count << std::endl;
return 0;
}
// 使用标准库的bind
#include <asio.hpp>
#include <iostream>
#include <functional>

void print(asio::steady_timer* t, int* count){
    if(*count < 5){
        std::cout << *count << std::endl;
        ++(*count);
        t->expires_at(t->expiry() + asio::chrono::seconds(1));
        t->async_wait(std::bind(print,
            t, count));
    }
}

int main(){
    asio::io_context io;
    int count = 0;
    asio::steady_timer t(io, asio::chrono::seconds(1));
    t.async_wait(std::bind(print,
                &t, &count));
    io.run();
    std::cout << "Final count is " << count << std::endl;
    return 0;
}
  • 38
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值