博客转载自:http://blog.csdn.net/yockie/article/details/40386145

先跟着boost文档中asio章节的指南中的几个例子学习一下使用:

所有的Asio类只要简单的包含"asio.hpp"头文件便可使用:#include <boost/asio.hpp>

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

使用Asio的所有程序都至少需要一个提供访问I/O功能的io_service 对象。因此在主函数中我们做的第一件事就是声明一个这个类型的对象:boost::asio::io_service io;

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

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

可以看一下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());
boost::asio::deadline_timer t(io, boost::posix_time::seconds());

在这个简单的程序中,我们用定时器演示一个阻塞等待。

#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());
t.wait(); std::cout << "Hello, world!\n"; return ;
}

deadline_timer::wait() 函数调用直到定时器终止(从定时器被创建算起,五秒后终止)才会返回。一个deadline timer 通常是下面两种状态中的一种:"expired(终止)" 或"not expired(不终止)"。
如果deadline_timer::wait() 函数被一个已经终止的定时器调用, 它将立即返回。

2. Timer.2 - 使用异步定时器

本例使用Asio的异步回调功能在定时器中演示一个异步等待。

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

最后我们打印出 "Hello, world!" 信息以显示定时器已经终止。

void print(const boost::system::error_code& /*e*/)
{
std::cout << "Hello, world!\n";
} int main()
{
boost::asio::io_service io; boost::asio::deadline_timer t(io, boost::posix_time::seconds());

接下来,我们调用 deadline_timer::async_wait() 函数执行一个异步等待去取代Timer.1例中的阻塞等待。当调用这个函数时我们传入上面定义的print回调句柄。

t.async_wait(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重新设置超时时间。

#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;
boost::asio::io_service io;
boost::asio::deadline_timer t(io, boost::posix_time::seconds()); t.async_wait(Print);
cout<<"to run"<<endl;
io.run();
cout<<"exit"<<endl;
return ;
}
结果:
2f98
to run (此处等了5s)
Hello World!
2f98 (说明是同一线程)
exit

3. 回调函数绑定参数

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

使用Asio实现一个重复定时器,你必须在你的回调函数中去改变定时器的终止时间,然后开始一个新的异步等待。显然这意味着回调函数必须拥有改变定时器对象的权限。为此我们为 print函数增加两个新参数:

(1) 一个指向定时器对象的指针。 
(2) 一个用于当定时器第6次被激活时我们可以中止程序的计数器。

void print(const boost::system::error_code& /*e*/,boost::asio::deadline_timer* t, int* count)
{
  if (*count < )
  {
    std::cout << *count << "\n";
    ++(*count)
  }
}

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

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

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

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

t->async_wait(boost::bind(print,boost::asio::placeholders::error, t, count));
#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 < )
{
cout<<"count = "<<*pcount<<endl;
cout<<boost::this_thread::get_id()<<endl;
(*pcount) ++; pt->expires_at(pt->expires_at() + boost::posix_time::seconds()) ; 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());
int count = ;
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 ;
}
结果:
14d0
to run (等了5s)
count =
14d0 (等了5s)
count =
14d0 (等了5s)
count =
14d0 (等了5s)
Final count is
exit

4. 多线程同步回调 

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

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

下面是Asio在单线程程序中的局限性,尤其是服务器方面,包括:

(1)操作需要较长时间处理才能完成时弱响应。 
(1)在大规模的多处理机系统中表现不佳。

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

让我们从定义一个名为printer的类开始,这与前一个示例中的类很相似。这个类是上一个例子的扩展,这里我们使用两个并行的定时器。

class CPrinter

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

CPrinter(boost::asio::io_service &io)
:m_strand(io)
,m_timer1(io, boost::posix_time::seconds())
,m_timer2(io, boost::posix_time::seconds())
,m_count()
{ m_timer1.async_wait(m_strand.wrap(boost::bind(&CPrinter::Print1, this) ));
m_timer2.async_wait(m_strand.wrap(boost::bind(&CPrinter::Print2, this) ));
} ~CPrinter()
{
cout<<"m_count = "<<m_count<<endl;
}

当开始同步操作时,每一个回调句柄都使用boost::asio::strand对象进行“包装”。strand::wrap() 函数返回一个新的通过boost::asio::strand对象自动分派的内部句柄。 通过同一boost::asio::strand对象对句柄进行“ 包装”,我们可以保证操作不会并发执行。
在一个多线程程序中,当访问同一共享资源时,异步操作必须是同步的。在本例中,print1 和print2)函数使用的共享资源std::cout 和count_数据成员。

void Print1()
{
if (m_count < )
{
cout<<"Timer1 count = "<<m_count<<endl;
cout<<boost::this_thread::get_id()<<endl;
m_count++; m_timer1.expires_at(m_timer1.expires_at() + boost::posix_time::seconds()) ; m_timer1.async_wait(m_strand.wrap(boost::bind(&CPrinter::Print1, this) ) );
}
} void Print2()
{
if (m_count < )
{
cout<<"Timer2 count = "<<m_count<<endl;
cout<<boost::this_thread::get_id()<<endl;
m_count++; m_timer2.expires_at(m_timer2.expires_at() + boost::posix_time::seconds()) ; m_timer2.async_wait(m_strand.wrap(boost::bind(&CPrinter::Print2, this) ) );
}
}

main函数中, io_service::run() 现在被两个线程调用:主线程和一个附加线程。这一切依赖于boost::thread对象来完成。 
正如它被一个单线程调用一样,io_service::run()的并发调用会一直持续到无任何“工作”可做。后台线程直到所有异步操作都完成后才会退出。

#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
using namespace std; class CPrinter
{
public:
CPrinter(boost::asio::io_service &io)
:m_strand(io)
,m_timer1(io, boost::posix_time::seconds())
,m_timer2(io, boost::posix_time::seconds())
,m_count()
{
m_timer1.async_wait(m_strand.wrap(boost::bind(&CPrinter::Print1, this) ) );
m_timer2.async_wait(m_strand.wrap(boost::bind(&CPrinter::Print2, this) ) );
}
~CPrinter()
{
cout<<"m_count = "<<m_count<<endl;
} void Print1()
{
if (m_count < )
{
cout<<"Timer1 count = "<<m_count<<endl;
cout<<boost::this_thread::get_id()<<endl;
m_count++; m_timer1.expires_at(m_timer1.expires_at() + boost::posix_time::seconds()) ; m_timer1.async_wait(m_strand.wrap(boost::bind(&CPrinter::Print1, this) ) );
}
} void Print2()
{
if (m_count < )
{
cout<<"Timer2 count = "<<m_count<<endl;
cout<<boost::this_thread::get_id()<<endl;
m_count++; m_timer2.expires_at(m_timer2.expires_at() + boost::posix_time::seconds()) ; m_timer2.async_wait(m_strand.wrap(boost::bind(&CPrinter::Print2, this) ) );
}
}
private:
boost::asio::strand m_strand;
boost::asio::deadline_timer m_timer1;
boost::asio::deadline_timer m_timer2;
int m_count; }; int main()
{
cout<<boost::this_thread::get_id()<<endl;
boost::asio::io_service io;
CPrinter cp(io); cout<<"to run"<<endl; boost::thread td(boost::bind(&boost::asio::io_service::run, &io)); io.run(); cout<<"exit"<<endl;
return ;

运行结果

620c
to run
Timer2 count =
79c4
Timer1 count =
620c
Timer1 count =
620c
Timer2 count =
79c4
Timer2 count =
79c4
Timer1 count =
620c
Timer1 count =
620c
Timer2 count =
79c4
Timer2 count =
79c4
Timer1 count =
620c
exit
m_count =

说明:

(1)两个Timer确实是在不同线程中执行,并且只有一个print操作执行完成之后才允许进入另一个print操作

(2)Timer1始终在一个线程中执行,Timer2始终在另一个线程中执行,(但不一定就是Timer1在主线程执行,这个分配时随机的)

注意:

deadline_timer和socket一样,都用io_service作为构造函数的参数。在其上进行异步操作,都将导致和io_service所包含的iocp相关联。这同样意味着在析构 io_service之前,必须析构关联在这个io_service上的deadline_timer

引申:

关于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技术文档》

【Boost】boost库中timer定时器 2的更多相关文章

  1. 【Boost】boost库中timer定时器 1

    博客转载自:http://blog.csdn.net/liujiayu2/article/details/50384537 同步Timer asio中提供的timer名为deadline_timer, ...

  2. 浅析linux内核中timer定时器的生成和sofirq软中断调用流程(转自http://blog.chinaunix.net/uid-20564848-id-73480.html)

    浅析linux内核中timer定时器的生成和sofirq软中断调用流程 mod_timer添加的定时器timer在内核的软中断中发生调用,__run_timers会spin_lock_irq(& ...

  3. 浅析linux内核中timer定时器的生成和sofirq软中断调用流程【转】

    转自:http://blog.chinaunix.net/uid-20564848-id-73480.html 浅析linux内核中timer定时器的生成和sofirq软中断调用流程 mod_time ...

  4. asp.net中Timer定时器在web中无刷新的使用

    最近在做一个项目的时候,web端的数据需要与数据源进行实时同步,并保证数据的准确性,当时,考虑到使用ajax异步刷新技术.但后来在网上查找相关资料时,发现这样做,太浪费资源了,因为ajax的提交请求不 ...

  5. C#中Timer定时器的使用示例

    关于C#中timer类 在C#里关于定时器类就有3个: 1.定义在System.Windows.Forms里 2.定义在System.Threading.Timer类里 3.定义在System.Tim ...

  6. .net中 Timer定时器

    作者:feiying008 在开发一套视觉系统时,发现系统内存一直不断增加,直至系统内存爆满.一开始还以为是程序内存泄露,是图像操作算法写的有问题,但是,发现如果电机轴如果 不运行的状态下,每隔一秒进 ...

  7. 关于C#中Timer定时器的重入问题解决方法(也适用于多线程)

    项目中用到了定时器随着服务启动作定时任务,按指定的准点时间定时执行相关操作,但是在指定准点时间内我只想让它执行一次,要避免重入问题的发生. 首先简单介绍一下timer,这里所说的timer是指的Sys ...

  8. Boost C++ 库 中文教程(全)

    Boost C++ 库 目录 第 1 章 简介 第 2 章 智能指针 第 3 章 函数对象 第 4 章 事件处理 第 5 章 字符串处理 第 6 章 多线程 第 7 章 异步输入输出 第 8 章 进程 ...

  9. 使用Boost库中的组件进行C++内存管理

    C++标准库中的auto_ptr,智能指针,部分的解决了获取资源自动释放的问题 在Boost中,提供了6中智能指针:scoped_ptr, scoped_array, shared_ptr, shar ...

随机推荐

  1. Github删除项目

    相关博客:GitLab删除项目操作 发现github的项目删除按钮挺难找的,记录一下. 1,先在github打开项目,进入项目 2,点击Settings,进去后往下拉就是删除按钮.

  2. KMSpico软件下载:激活Windows系统和Office工具

    每次都为激活Windows系统 和 Microsoft Offilce 工具头疼,为此特意记录并分享一下激活工具. 上资源:https://pan.baidu.com/s/1kI5YpCO2aYl-r ...

  3. I.MX6 make menuconfig进入x86模式

    /************************************************************************ * I.MX6 make menuconfig进入x ...

  4. LeetCode K-diff Pairs in an Array

    原题链接在这里:https://leetcode.com/problems/k-diff-pairs-in-an-array/#/description 题目: Given an array of i ...

  5. 清理svn.bat

     @echo on  color 2f  mode con: cols=80 lines=25  @REM  @echo 正在清理SVN文件,请稍候......  @rem 循环删除当前目录及子目录下 ...

  6. 7.Selenium+Python实现搜索百度的测试用例

    1.导入测试用例需要的模块,unittest是python的内置模块,它提供了组织测试用例的框架 import unittest # 导入测试用例的模块 2.测试用例继承于unittest class ...

  7. linux环境下搭建redis

    1. 官网下载安装包,然后解压,或者直接从github上pull下来. git clone https://github.com/antirez/redis.git 2. 确保linux环境上已安装g ...

  8. python中print的几种用法

    python中的print有几种常用的用法: 1. print("first example") 2. print("second", "exampl ...

  9. What makes an inferred latch? how To avoid creating inferred latches? when do you know you need latches?

    What makes an inferred latch?For combinatorial logic, the output of the circuit is a function of inp ...

  10. linux上安装gitolite和windows上安装tortoisegit及msysgit

    1 quick install+setup for experts If your Unix-fu and ssh-fu are good, just copy your ssh public key ...