C++Asio库学习 Timer.4~5

Timer.4 使用成员函数作为completion handler

在这个例子中,我们将看到如何使用一个类成员函数作为completion handler。这个程序应该与Timer.3 执行相同的操作。

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

不是像之前例子中定义一个自由函数print作为completion handler,我们现在定义一个printer的类。

class printer{
public:

这个类的构造函数将使用一个io_context对象的引用作为参数,并且使用它初始化timer_成员。这个用来关闭程序的计数器现在也是类的一个成员。

printer(asio::io_context& io)
    : timer_(io,asio::chrono::seconds(1)),
      count_(0){

boost::bind函数对于类成员函数也可以像自由函数那样发挥作用。因为所有非静态类成员函数都有一个隐式的this参数,我们需要向这个函数绑定this。正如timer.3,boost::bind将我们的completion handler转化为一个函数对象,仿佛它有签名void(const asio::error_code&)

你讲注意到asio::placeholders::error占位符在这里并没有出现,因为print成员函数并不接受一个错误对象作为参数。

timer_.async_wait(boost::bind(&printer::print, this));
}

在这个类的析构函数,我们将打印计数器最后的值。

~printer(){
    std::cout << "Final count is " << count_ << std::endl;
}

这个print成员函数与timer.3的print函数非常相似,除了它现在是在类的数据成员上操作,而不是传入timer和counter作为参数。

void print(){
    if(count_ < 5){
        std::cout << count_ << std::endl;
        ++count_;

        timer_.expires_at(timer_.expiry() + asio::chrono::seconds(1));
        timer.async_wait(boost::bind(&printer::print, this));
    }
}
private:
    asio::steady_timer timer_;
    int count_;
};

主函数比之前简单,如下

int main(){
    asio::io_context io;
    printer p(io);
    io.run();

    return 0;
}

使用标准库bind,如下:

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

class printer {
public:
    printer(asio::io_context& io)
        : timer_(io, 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() + asio::chrono::seconds(1));
            timer_.async_wait(std::bind(&printer::print, this));
        }
    }
private:
    asio::steady_timer timer_;
    int count_;
};

int main(){
    asio::io_context io;
    printer p(io);
    io.run();
    return 0;
}

Timer.5 在多线程程序中同步completion handler

这个示例展示strand类模板的使用来在多线程程序中同步completion handler。先前4个示例避免了handler的同步问题,通过只在一个线程中调用io_context::run()函数。正如你已经知道的,asio库保证了completion handler将只会在当前调用io_context::run()的线程中调用。因此,只在一个线程中调用io_context::run()确保了completion handler不能并发地运行。

单线程方案通常是一个好的起点,但它的负面是一些程序上的限制,尤其是服务器,包括:

  • 很差的灵敏性,当handlers需要花费大量时间完成
  • 不能扩展到多处理器系统

如果你发现自己陷入这些限制中,一个替代的方案是拥有一个线程池来调用io_context::run()。然而,因为这允许handlers并发执行,当handlers可能访问共享或是线程不安全的资源时,我们需要同步的方法。

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

我们从定义printer类开始。这个类将会扩展为并行执行2个timer。

class printer
{
public:

除了初始化一对asio::steady_timer成员,构造函数也需要初始化strand_成员,这是类型为asio::strandasio::io_context::executor_type的对象。

strand 类模板是一个执行器适配器,可以保证通过它分派的handler,在下一个handler启动之前,一个正在执行的handler将被允许完成。无论调用 io_context::run() 的线程数量有多少,都能保证这一点。当然,处理程序仍有可能与其他处理程序同时执行,而这些处理程序不是通过strand调度的,就是通过不同的strand对象调度的。

  printer(asio::io_context& io)
    : strand_(asio::make_strand(io)),
      timer1_(io, asio::chrono::seconds(1)),
      timer2_(io, asio::chrono::seconds(1)),
      count_(0)
  {

当发起异步操作时,每个completion handler都与一个asio::strandasio::io_context::executor_type对象相关联。这个asio::bind_executor()函数返回一个新的handler,它可以通过strand对象自动分发它所包含的handler。通过将handler绑定到相同的strand,我们确信它们不能并发执行。

    timer1_.async_wait(asio::bind_executor(strand_,
          boost::bind(&printer::print1, this)));

    timer2_.async_wait(asio::bind_executor(strand_,
          boost::bind(&printer::print2, this)));
  }

  ~printer()
  {
    std::cout << "Final count is " << count_ << std::endl;
  }

在一个多线程程序中,如果需要访问共享资源,异步操作的handler应该被同步。在这个示例中,由print1和2使用的共享资源时std::cout和count_成员。

  void print1()
  {
    if (count_ < 10)
    {
      std::cout << "Timer 1: " << count_ << std::endl;
      ++count_;

      timer1_.expires_at(timer1_.expiry() + asio::chrono::seconds(1));

      timer1_.async_wait(asio::bind_executor(strand_,
            boost::bind(&printer::print1, this)));
    }
  }

  void print2()
  {
    if (count_ < 10)
    {
      std::cout << "Timer 2: " << count_ << std::endl;
      ++count_;

      timer2_.expires_at(timer2_.expiry() + asio::chrono::seconds(1));

      timer2_.async_wait(asio::bind_executor(strand_,
            boost::bind(&printer::print2, this)));
    }
  }

private:
  asio::strand<asio::io_context::executor_type> strand_;
  asio::steady_timer timer1_;
  asio::steady_timer timer2_;
  int count_;
};

主函数现在在两个线程中触发io_context::run()函数,一个在主函数中,另一个在额外的线程中。

int main()
{
  asio::io_context io;
  printer p(io);
  asio::thread t(boost::bind(&asio::io_context::run, &io));
  io.run();
  t.join();

  return 0;
}

使用标准库的bind,如下:

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

class printer {
public:
    printer(asio::io_context& io):
        strand_(asio::make_strand(io)),
        timer1_(asio::steady_timer(io, asio::chrono::seconds(1))),
        timer2_(asio::steady_timer(io, asio::chrono::seconds(1))),
        count_(0){
            timer1_.async_wait(asio::bind_executor(strand_, std::bind(&printer::print1, this)));
            timer2_.async_wait(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() + asio::chrono::seconds(1));
            timer1_.async_wait(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() + asio::chrono::seconds(1));
            timer2_.async_wait(asio::bind_executor(strand_, std::bind(&printer::print2, this)));
        }
    }
private:
    asio::strand<asio::io_context::executor_type> strand_;
    asio::steady_timer timer1_;
    asio::steady_timer timer2_;
    int count_;
};

int main(){
    asio::io_context io;
    printer p(io);
    asio::thread t([&io](){ io.run(); });
    io.run();
    t.join();
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值