转自:https://www.cnblogs.com/xuaidongstdudyrecording/p/6077639.html
boost::asio::deadline_timer
并发与并行: 并发和并行从宏观上来讲都是同时处理多路请求的概念。但并发和并行又有区别,并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔内发生。

所有的Asio类只要简单的包含"asio.hpp"头文件便可使用:

#include <boost/asio.hpp>

使用Asio类的所有程序都至少需要一个提供访问I/O功能的io_service 对象。

boost::asio::io_service io;

1. 同步定时器

本程序中使用了定时器,我们需要包含相应的的Boost.Date_Time 头文件来处理时间操作:

#include <boost/date_time/posix_time/posix_time.hpp>

接下来我们声明一个boost::asio::deadline_timer类型的对象。作为 Asio的核心类,它提供的I/O功能(在此为定时器功能)通常用一个io_service 的引用作为其构造函数的第一个参数。第二个参数设置一个从现在开始5秒后终止的定时器。

boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));

boost::asio::deadline_timer的几个构造函数

basic_deadline_timer(  boost::asio::io_service & io_service);  basic_deadline_timer(  boost::asio::io_service & io_service,  const time_type & expiry_time);  basic_deadline_timer(  boost::asio::io_service & io_service,  const duration_type & expiry_time);

注意后两种的区别,说明以下2种用法是等价的:

boost::asio::deadline_timer t(io, boost::posix_time::microsec_clock::universal_time()+boost::posix_time::seconds(5));
boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));

定时器使用举例:

#include <iostream>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>  int main()
{  boost::asio::io_service io;  boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));  t.wait();  std::cout << "Hello, world!\n";  return 0;
}

代码解释:
在这个简单的程序中,我们用定时器演示一个阻塞等待。deadline_timer::wait() 函数调用直到定时器终止(从定时器被创建算起,五秒后终止)才会返回。
一个deadline_timer 通常是下面两种状态中的一种:“expired(终止)” 或"not expired(不终止)"。如果deadline_timer::wait() 函数被一个已经终止的定时器调用, 它将立即返回。

2. 异步定时器

代码举例:本例使用Asio的异步回调功能在定时器中演示一个异步等待。
使用Asio的异步功能意味着当一个异步操作完成时一个回调函数将被调用。在本程序中我们定义一个名为Print 的函数,在异步等待结束后这个函数将被调用。

#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
using namespace std;    void Print(const boost::system::error_code &ec)
{  cout<<"Hello World!"<<endl;  cout<<boost::this_thread::get_id()<<endl;
}
int main()
{    cout<<boost::this_thread::get_id()<<endl;//主线程IDboost::asio::io_service io;  boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));  t.async_wait(Print);  //异步等待cout<<"to run"<<endl;  io.run();  cout<<"exit"<<endl;  return 0;
}

执行结果:

2f98
to run(此处等了5s)
Hello World!
2f98  (说明是同一线程)
exit

解释说明:
首先,调用 deadline_timer::async_wait() 函数执行一个异步等待,取代上1例中的阻塞等待。当调用这个函数时我们传入定义的print回调句柄。
最后,必须在io_service对象上调用io_service::run()成员函数。

Asio保证回调句柄仅仅能被io_service::run()启动的当前线程所调用。 因此,如果io_service::run() 函数不执行,用于异步等待完成时的回调函数将永远不会被调用。

当仍旧有“工作”可做时,io_service::run() 函数会继续运行。在本例中,“工作”是定时器的异步等待,因此,直到定时器终止和回调函数执行完成,程序才会返回。

在调用io_service::run()之前确保给 io_service 一些工作去做,这非常重要。 例如,如果我们省略了上面调用的deadline_timer::async_wait() 函数,io_service对象将没有任何事情去做,因此io_service::run() 将立即返回。

和同步方式相比,异步方式主要有两点不同:
(1) 调用的是非阻塞函数async_wait,它的入参是一个回调函数。
(2) 显式调用io_service.run()函数驱动异步IO调度。

值得提出的是,异步回调函数handler的参数中有一个error,注意这个error很重要,表明这个handler是因为超时被执行还是因为被cancel。

符合2种情况之一,handler被执行:超时或者被cancel(可以通过此error的值进行区分)。
这同时隐含的说明了除非io.stop被调用,否则handler一定会被执行。即便是被cancel。
被cancel有多种方法,直接调用cancel或者调用expires_at,expires_from_now重新设置超时时间。

3. 重复异步定时器

本例使定时器每秒被激活一次。例子将示范如何给函数指针传递附加参数。

#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
using namespace std;    void Print(const boost::system::error_code &ec,  boost::asio::deadline_timer* pt,  int * pcount)
{  if (*pcount < 3)  {  cout<<"count = "<<*pcount<<endl;  cout<<boost::this_thread::get_id()<<endl;  (*pcount) ++;   pt->expires_at(pt->expires_at() + boost::posix_time::seconds(5)) ;    pt->async_wait(boost::bind(Print, boost::asio::placeholders::error, pt, pcount));            }
}
int main()
{    cout<<boost::this_thread::get_id()<<endl;  boost::asio::io_service io;  boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));  int count = 0;  t.async_wait(boost::bind(Print, boost::asio::placeholders::error, &t, &count));  cout<<"to run"<<endl;  io.run();  cout << "Final count is " << count << "\n";  cout<<"exit"<<endl;  return 0;
}

程序执行结果:

14d0
to run(等了5s)
count = 0
14d0(等了5s)
count = 1
14d0(等了5s)
count = 2
14d0(等了5s)
Final count is 3
exit

解释说明:
使用Asio实现一个重复定时器,你必须在你的回调函数中去改变定时器的终止时间,然后开始一个新的异步等待。显然这意味着回调函数必须拥有改变定时器对象的权限。为此我们为 print函数增加两个新参数:
(1) 一个指向定时器对象的指针。
(2) 一个用于当定时器第4次被激活时我们可以中止程序的计数器。

void print(const boost::system::error_code& /*e*/,boost::asio::deadline_timer* t, //定时器对象的指针int* count//中止程序的计数器
)

如上所示,示例程序使用了一个计数器count,当定时器被第4次激活时,用来中止程序。然而,这里并没有显式地要求io_service对象中止。会以示例2中,当没有更多“工作”去做时,io_service::run() 函数完成。在计数器达到4时,定时器并没有启动一个新的异步等待。该io_service执行完工作后停止运行。

if (*pcount < 3)

接着,我们推迟定时器的终止时间。通过在原先的终止时间上增加延时,我们可以确保定时器不会在处理回调函数所需时间内到期。

t->expires_at(t->expires_at() + boost::posix_time::seconds(1));

接着我们在定时器中启动一个新的异步等待。我们必须使用boost::bind() 函数给你的回调函数绑定额外的参数,因为deadline_timer::async_wait() 函数只期望得到一个拥用 void(const boost::system::error_code&) 签名的函数指针(或函数对象)。为你的print函数绑定附加的参数后,它就成为与签名精确匹配的函数对象。
在本例中,boost::bind()的boost::asio::placeholders::error参数是为了给回调函数传入一个error对象。当开始异步操作时,如果使用boost::bind(),你必须指定和回调函数的参数列表相匹配的一个参数。在示例4中,如果在回调函数中,这个参数不是必需的,这个占位符会被省略。

4. 多线程同步回调

本例示范了使用boost::asio::strand 类来创建多线程程序中的同步回调句柄。

前几个例程只是在单线程下使用io_service::run() 函数来避免处理函数同步。 如你所见,Asio库保证回调句柄仅能被当前正在调用 io_service::run(). 函数的线程调用。 因此,在单线程中调用io_service::run() 能确保回调句柄不被并发运行。

下面是Asio在单线程程序中的局限性,尤其是服务器方面,包括:
(1)操作需要较长时间处理才能完成时弱响应。
(1)在大规模的多处理机系统中表现不佳。

如果你发现自己陷入这些局限时,一个可供选择的方法是创建一个每个线程都调用io_service::run() 的线程池。 不过,因为这允许并发操作,当访问一个共享、非线程安全的资源时,我们需要一个同步方式。

代码举例:

#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/thread/thread.hpp>boost::asio::io_service Service;class Timer
{private:boost::asio::deadline_timer timer1;boost::asio::deadline_timer timer2;boost::asio::strand m_strand;int count;public:Timer(boost::asio::io_service& ref_service):timer1(ref_service, boost::posix_time::seconds(3)), timer2(ref_service, boost::posix_time::seconds(3)), m_strand(ref_service), count(0){timer1.async_wait(m_strand.wrap(boost::bind(&Timer::Print1, this, boost::asio::placeholders::error)));timer2.async_wait(m_strand.wrap(boost::bind(&Timer::Print2, this, boost::asio::placeholders::error)));}~Timer(){}void Print1(const boost::system::system_error& error){if (count < 5){std::cout << "Pints1's count:" << count<<std::endl;std::cout << boost::this_thread::get_id() <<std:: endl;++count;timer1.expires_at(timer1.expires_at() + boost::posix_time::seconds(1));timer1.async_wait(m_strand.wrap(boost::bind(&Timer::Print1, this, boost::asio::placeholders::error)));}else{std::cout << "the Error is:" << error.what() << std::endl;}}void Print2(const boost::system::system_error& error){if (count < 5){std::cout << "Print2's count :" <<count<< std::endl;std::cout << boost::this_thread::get_id() << std::endl;++count;timer2.expires_at(timer2.expires_at() + boost::posix_time::seconds(1));timer2.async_wait(m_strand.wrap(boost::bind(&Timer::Print2, this, boost::asio::placeholders::error)));}else{std::cout << "the Error is:" << error.what() << std::endl;boost::asio::io_service::work work(Service);}}};int main(int argc, char** argv)
{std::cout << boost::this_thread::get_id() << std::endl;Timer m_timer(Service);std::cout << "异步给主线程运行到io_Service::run之前,直到异步回调函数调完" << std::endl;boost::thread t([&](){ std::cout << "\n进入t线程内部执行" << boost::this_thread::get_id() << std::endl; Service.run();std::cout << "t线程内部:异步给主线程运行到io_Service::run之前直到道异步回调函数调完  (验证)!" << std::endl; });t.detach();std::cout << "异步给主线程运行到io_Service::run之前,直到异步回调函数调完" << std::endl;Service.run();std::cout << "异步给主线程运行到io_Service::run之前,直到异步回调函数调完    (验证)!" << std::endl;getchar();return 0;
}

程序执行结果

2544
异步给主线程运行到io_Service::run之前,直到异步回调函数调完
进入t线程内部执行23d4Print2's count :0
23d4
Pints1's count:1
2544
Pints1's count:2
2544
Print2's count :3
23d4
Print2's count :4
23d4
the Error is:操作成功完成。
the Error is:操作成功完成。
异步给主线程运行到io_Service::run之前,知道异步回调函数调完    (验证)!
t线程内部:异步给主线程运行到io_Service::run之前,知道异步回调函数调完    (验证)!

解释说明
在类Timer中,初始化一对boost::asio::deadline_timer 成员变量外,构造函数还初始化一个boost::asio::strand类型strand_ 成员变量。
boost::asio::strand 对象保证:对于通过它来分派执行的众操作中,只有一个操作执行完成之后才允许进入下一个操作。 这种保证与多少个线程调用io_service::run() 无关。当然,如果不是通过一个boost::asio::strand对象分派, 或者通过其它不同的boost::asio::strand对象分派,这些操作仍旧可能是并发的。

当开始同步操作时,每一个回调句柄都使用boost::asio::strand对象进行“包装”。strand::wrap() 函数返回一个新的通过boost::asio::strand对象自动分派的内部句柄。 通过同一boost::asio::strand对象对句柄进行“ 包装”,我们可以保证操作不会并发执行。

timer1.async_wait(m_strand.wrap(boost::bind(&Timer::Print1, this, boost::asio::placeholders::error)));
timer2.async_wait(m_strand.wrap(boost::bind(&Timer::Print2, this, boost::asio::placeholders::error)));

在一个多线程程序中,当访问同一共享资源时,异步操作必须是同步的。在本例中,print1 和print2)函数使用的共享资源std::cout 和count_数据成员。

main函数中, io_service::run() 现在被两个线程调用:主线程和一个附加线程。这一切依赖于boost::thread对象来完成。

正如它被一个单线程调用一样,io_service::run()的并发调用会一直持续到无任何“工作”可做。后台线程直到所有异步操作都完成后才会退出。

此外:
(1)两个Timer确实是在不同线程中执行,并且只有一个print操作执行完成之后才允许进入另一个print操作
(2)Timer1始终在一个线程中执行,Timer2始终在另一个线程中执行,(但不一定就是Timer1在主线程执行,这个分配时随机的)

引申:
关于boost::asio::strand,有三个函数:post, dispatch, wrap

post: 不管什么情况都会把任务丢到队列中,然后立即返回。如:m_strand.post(boost::bind(print, 2));
dispatch: 如果跟run()在一个线程,那么任务会直接在dispatch内部调用,执行结束后返回。不在一个线程跟post一样。如:m_strand.dispatch(boost::bind(print, 1));

wrap:返回一个新的通过boost::asio::strand对象自动分派的内部句柄

BOOST库介绍(八)——deadline_timer相关推荐

  1. boost库介绍以及使用

    C++ boost库介绍以及使用 一. 什么是boost库 ​ boost 库是一个优秀的,可移植的,开源的 C++ 库,它是由 C++ 标准委员会发起的,其中一些内容已经成为了下一代 C++ 标准库 ...

  2. BOOST库介绍,安装

    第1讲boost库介绍 boost是一个准标准库,相当于STL的延续和扩充,它的设计理念和STL比较接近,都是利用泛型让复用达到最大化.不过对比STL,boost更加实用. STL集中在算法部分,而b ...

  3. 最新超详细C++经典Boost库介绍

    Boost库 Boost库是为C++语言标准库提供扩展的一些C++程序库的总称,由Boost社区组织开发.维护.Boost库可以与C++标准库完美共同工作,并且为其提供扩展功能. 目录 Boost库 ...

  4. C++ boost库介绍以及开发环境搭建

    一.什么是boost库 boost 库是一个优秀的,可移植的,开源的 C++ 库,它是由 C++ 标准委员会发起的,其中一些内容已经成为了下一代 C++ 标准库的内容,在 C++ 社区中影响甚大,是一 ...

  5. boost库下的deadline_timer和steady_timer 区别

    1:steady_timer的expires_from_now函数参数必须使用std::chrono 2:deadline_timer的expires_from_now函数参数必须使用boost::p ...

  6. Boost库编译安装

    一.Boost库介绍 Boost库是一个经过千锤百炼.可移植.提供源代码的C++库,作为标准库的后备,是C++标准化进程的发动机之一.Boost库由C++标准委员会库工作组成员发起,其中有些内容有望成 ...

  7. Boost(1):Boost库简介及安装

    1. Boost库介绍 Boost是一个功能强大.构造精巧.跨平台.开源并且完全免费的C++程序库,在1998年由Beman G.Dawes发起倡议并建立.使用了许多现代C++编程技术,内容涵盖字符串 ...

  8. Boost(2):boost.python库介绍及简单示例

    1. boost.python介绍 将c/c++的函数接口转换为Python接口有好几种解决方案,不同于C语言的简单直接,C++因使用了大量的面向对象编程的思想导致转换为Python接口时相对比较复杂 ...

  9. (八)boost库之异常处理

    当你面对上千万行的项目时,当看到系统输出了异常信息时,你是否想过,如果它能将文件名.行号等信息输出,该多好啊,曾经为此绞尽脑汁. 今天使用boost库,将轻松的解决这个问题. 1.boost异常的基本 ...

  10. 基于boost库的搜索引擎

    文章目录 一.项目介绍 二.搜索引擎相关的宏观原理 三.搜索引擎技术栈和项目环境 四.正排索引vs倒排索引-搜索引擎的具体原理 五.编写数据去标签与数据清洗的模块Parser 1.下载数据源 2.建立 ...

最新文章

  1. 论电子计算机在审计中的应用,计算机技术在审计中的应用领域分析.doc
  2. html实现以秒为单位倒数,跳转新的页面
  3. 洛谷——P1951 收费站_NOI导刊2009提高(2)
  4. DeepLearning.AI笔记:二、神经网络编程基础
  5. 【BZOJ】 3238: [Ahoi2013]差异
  6. Java –远景JDK 8
  7. mybatis动态sql中的where标签的使用
  8. 动手学深度学习(PyTorch实现)(十)--NiN模型
  9. python开发sqlite3完整_python开发_sqlite3_绝对完整
  10. BFC(块级化上下文)
  11. linux 用户设密码,linux 上添加用户,设置密码
  12. php mysql 数据库类_PHP操作MySQL数据库的类
  13. java 多线程任务队列_精简的java 线程池与任务队列
  14. cnn视频下载软件附代码
  15. 在 Windows 下关闭135/139/445端口的图文方法
  16. Python基础(8)字符串及常用操作
  17. 《人月神话》(P8)巴比伦塔的失败
  18. Java设计原则——依赖倒转原则
  19. Android P+通过反射调用系统API实现高级功能
  20. 漏洞分析丨HEVD-0x6.UninitializedStackVariable[win7x86]

热门文章

  1. myeclipse 8.0GA 安装注册步骤
  2. 直连AIX服务器,对接AIX链路问题
  3. 精品推荐:【CKEditor】全球最优秀的网页在线文字编辑器之一
  4. 原型和原型链的理解(通俗易懂)
  5. abp vnext token失效时间设置
  6. 从openjdk.java.net获取OpenJDK8源码并编译(amd64/aarch64/arm64)
  7. JAVA RSA加密解密代码范例(byte[]版)
  8. gtk/gtk.h 没有那个文件或目录
  9. VirtualBox虚拟机如何选中“启用嵌套 VT-x/AMD-V”
  10. 虚拟机中编译代码死机,加大内存解决