天天看点

基于Asio库的定时器,封装实现好用的定时任务

ASIO库介绍

a cross-platform C++ library for network。

Asio 是一个好用的、常用的跨平台的 C++ 库,常用于网络编程、底层的 I/O 编程等 (low-level I/O)。结构框架如下:

基于Asio库的定时器,封装实现好用的定时任务

详细介绍:xBoost.Asio - 1.78.0

分为boost版和非boost版(不依赖boost库)。

windows下的下载安装推荐使用vcpkg这个工具,vcpkg是命令行包管理工具。

vcpkg工具介绍

在使用第三方库的c或c++开发中可以简化相关的配置操作。vcpkg安装的包支持vs2015和vs2017工具集,目前在windows平台已有超过900多个包,linux平台超过350个包。在默认情况下,vcpkg会优先使用vs2017进行编译。如果未安装,则使用vs2015编译和安装。使用vcpkg对于c或c++开发,令人烦恼的第三方包管理工作大大的减轻。

# vcpkg工具安装,下载vcpkg项目
git clone https://github.com/Microsoft/vcpkg
# 本地编译(windows最好在powershell下)
./bootstrap-vcpkg.bat
# 搜索想要安装的包
vcpkg search asio
# 安装指定的包,包分号后面的表示架构,可用的值为之前列出的那些
vcpkg install asio:x86-windows
# 列出已经安装的包
vcpkg list
# 已安装的包更新
vcpkg upgrade
# 删除已安装的包
vcpkg remove asio:x86-windows           

复制

配合cmake使用时注意配置CMAKE_TOOLCHAIN_FILE是否设置为了vcpkg.cmake文件路径。

基于Asio库的定时器封装

工作中,定时任务和定时执行是很常见的功能需求。asio库提供了timer定时器功能asio::steady_timer,可以实现同步和异步的调用机制,但是不封装一下不太好用。

比如以下使用,同步使用:

第一个参数是asio::io_context,第二个参数设置定时器现在开始3秒后终止。wait()是一个阻塞等待,3秒后定时器终止时返回。接着打印“Hello, world!”。

int main()
{  
    asio::io_context ioc;
    asio::steady_timer timer(ioc, std::chrono::seconds(3));
    timer.wait();
    std::cout << "Hello, world!" << std::endl;
    return 0;
}           

复制

异步使用时:

async_wait() 执行异步等待,设置回调函数Print,当异步操作结束后(此处即定时器结束后)该函数会被调用。Asio保证回调句柄仅仅能被run()启动的当前线程所调用。如果run() 函数不执行,用于异步等待完成时的回调函数(此处即Print())将永远不会被调用。

void Print(std::error_code ec) {
    std::cout << "Hello, world!" << std::endl;
}
 
int main()
{  
    asio::io_context ioc;
    asio::steady_timer timer(ioc, std::chrono::seconds(3));
    timer.async_wait(&Print);   
    ioc.run();
    return 0;
}           

复制

下例中每隔1秒打印一次计数,从0到2。

async_wait回调函数的签名为 void (std::error_code),传递额外的参数时需要使用bind。Print函数中,计数小于3时,expires_at()推迟定时器的终止时间。async_wait()启动一个新的异步等待。计数大于3时,run()函数返回。

void Print(std::error_code ec, asio::steady_timer* timer, int *count) 
{
    if (*count < 3)
    {
        std::cout << *count << std::endl;
        ++(*count);       
        timer->expires_at(timer->expires_at() + std::chrono::seconds(1));
        timer->async_wait(std::bind(&Print, std::placeholders::_1, timer, count));
    }
}
int main(int argc, char** argv)
{  
    asio::io_context ioc;
    asio::steady_timer timer(ioc, std::chrono::seconds(3));
    int count = 0;
    timer.async_wait(std::bind(&Print,std::placeholders::_1, &timer, &count));
    ioc.run();
    return 0;
}           

复制

封装过程

封装后可以直接在回调函数中执行定时的任务了,方便很多,大大简化了使用过程。且可以设置是定时周期执行或者是定时执行指定次数。

#ifndef ARMCORE_INVOKETIMER_HPP
#define ARMCORE_INVOKETIMER_HPP

#include <functional>
#include <memory>
#include <logging.hpp>

#include <asio.hpp>

namespace awesome_asio
{
class InvokeTimer;

typedef std::shared_ptr<InvokeTimer> InvokeTimerPtr;
typedef std::weak_ptr<InvokeTimer> InvokeTimerWPtr;

class InvokeTimer : std::enable_shared_from_this<InvokeTimer>
{
public:
  virtual ~InvokeTimer() {}

  typedef std::function<void()> Function;
  static InvokeTimerPtr CreateTimer(asio::io_context &io, const asio::steady_timer::duration &duration, bool period,
                                    Function &f)
  {
    InvokeTimerPtr it(new InvokeTimer(io, duration, f, period));
    it->self_ = it;
    return it;
  }
  static InvokeTimerPtr CreateTimer(asio::io_context &io, const asio::steady_timer::duration &duration, bool period,
                                    Function &&f)
  {
    InvokeTimerPtr it(new InvokeTimer(io, duration, std::move(f), period));
    it->self_ = it;
    return it;
  }
  static InvokeTimerPtr CreateTimer(asio::io_context &io, const asio::steady_timer::duration &duration,
                                    std::size_t count, Function &f)
  {
    InvokeTimerPtr it(new InvokeTimer(io, duration, f, count));
    it->self_ = it;
    return it;
  }
  static InvokeTimerPtr CreateTimer(asio::io_context &io, const asio::steady_timer::duration &duration,
                                    std::size_t count, Function &&f)
  {
    InvokeTimerPtr it(new InvokeTimer(io, duration, std::move(f), count));
    it->self_ = it;
    return it;
  }

  void Start()
  {
    start_ = std::chrono::system_clock::now();
    timer_.async_wait(std::bind(&InvokeTimer::OnTrigger, this, std::placeholders::_1));
  }

  void Cancel()
  {
    periodic_ = false;

    timer_.cancel();
  }

  inline std::size_t getTickCount() { return trigger_count_; }
  inline int64_t getElapsedMillionsecond() const
  {
    auto end = std::chrono::system_clock::now();
    return std::chrono::duration_cast<std::chrono::milliseconds>(end - start_).count();
  }
  inline int64_t getElapsedSecond() const
  {
    auto end = std::chrono::system_clock::now();
    return std::chrono::duration_cast<std::chrono::seconds>(end - start_).count();
  }
  inline bool isTriggered() const { return isTriggered_; }

private:
  explicit InvokeTimer(asio::io_context &io, const asio::steady_timer::duration &duration, Function &f, bool period)
      : timer_(io, duration),
        duration_(duration),
        periodic_(period),
        callback_(f),
        count_(0),
        trigger_count_(0),
        isTriggered_(false)
  {
  }
  explicit InvokeTimer(asio::io_context &io, const asio::steady_timer::duration &duration, Function &&f, bool period)
      : timer_(io, duration),
        duration_(duration),
        periodic_(period),
        callback_(std::move(f)),
        count_(0),
        trigger_count_(0),
        isTriggered_(false)
  {
  }

  explicit InvokeTimer(asio::io_context &io, const asio::steady_timer::duration &duration, Function &f,
                       std::size_t count)
      : timer_(io, duration),
        duration_(duration),
        periodic_(true),
        callback_(f),
        count_(count),
        trigger_count_(0),
        isTriggered_(false)
  {
  }

  explicit InvokeTimer(asio::io_context &io, const asio::steady_timer::duration &duration, Function &&f,
                       std::size_t count)
      : timer_(io, duration),
        duration_(duration),
        periodic_(true),
        callback_(std::move(f)),
        count_(count),
        trigger_count_(0),
        isTriggered_(false)
  {
  }

  void OnTrigger(const asio::error_code &e)
  {
    if (e.value() != 0)
    {
      self_.reset();
      return;
    }

    isTriggered_ = true;
    trigger_count_++;

    try {
      if (callback_)
        callback_();
    } catch (const std::error_code& errorCode) {
      LOGGING_ERROR("[FATAL] errorCode: %s", errorCode.message());
    }

    if (periodic_)
    {
      if (count_ == 0 || trigger_count_ < count_)
      {
        timer_.expires_at(timer_.expiry() + duration_);
        Start();
      }
      else if (trigger_count_ < count_)
      {
        timer_.expires_at(timer_.expiry() + duration_);
        Start();
      }
      else
        self_.reset();
    }
    else
    {
      self_.reset();
    }
  }

private:
  asio::steady_timer timer_;
  asio::steady_timer::duration duration_;
  bool periodic_;
  std::shared_ptr<InvokeTimer> self_;
  Function callback_;
  std::size_t count_;
  std::size_t trigger_count_;
  std::chrono::system_clock::time_point start_;
  bool isTriggered_;
};

}  // namespace awesome_asio           

复制

使用方法

//......
{  
    asio::io_context ioc;
    //......
    InvokeTimer::CreateTimer(ioc,std::chrono::milliseconds(100), true, [this] {
				 cout <<"do someting period duration 100ms.."<< endl;				  
										
	});
    
}           

复制

引用:

Boost.Asio - 1.78.0

Windows10下配置Boost_卖萌的大米的博客-CSDN博客_boost windows

vcpkg使用详解_weixin_34293246的博客-CSDN博客

vcpkg 详细介绍_零点零一的博客-CSDN博客_vcpkg是什么

基于Asio 的定时器( asio::steady_timer )_万里归来少年心的博客-CSDN博客_asio 定时器

vcpkg国内镜像使用方法——解决国内下载慢的问题_灵魂制造者的博客-CSDN博客_vcpkg 源