boost::asio::deadline_timer(理解)
并发与并行: 并发和并行从宏观上来讲都是同时处理多路请求的概念。但并发和并行又有区别,并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔内发生。
1.Timer.1 - 使用同步定时器
先完整介绍一下,后面的例子该省略的就省略了。
所有的Asio类只要简单的包含"asio.hpp"头文件便可使用:
#include <boost/asio.hpp>
因为本程序中使用了定时器,我们需要包含相应的的Boost.Date_Time 头文件来处理时间操作:
#include <boost/date_time/posix_time/posix_time.hpp>
使用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(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());
boost::asio::deadline_timer t(io, boost::posix_time::seconds());
在这个简单的程序中,我们用定时器演示一个阻塞等待。deadline_timer::wait() 函数调用直到定时器终止(从定时器被创建算起,五秒后终止)才会返回。
一个deadline timer 通常是下面两种状态中的一种:"expired(终止)" 或"not expired(不终止)"。如果deadline_timer::wait() 函数被一个已经终止的定时器调用, 它将立即返回。
t.wait();
最后我们打印出 "Hello, world!" 信息以显示定时器已经终止。
std::cout << "Hello, world!\n";
代码:
#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 ;
}
2. Timer.2 - 使用异步定时器
本例使用Asio的异步回调功能在定时器中演示一个异步等待。
使用Asio的异步功能意味着当一个异步操作完成时一个回调函数将被调用。在本程序中我们定义一个名为print 的函数,在异步等待结束后这个函数将被调用。
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(5));
接下来,我们调用 deadline_timer::async_wait() 函数执行一个异步等待去取代Timer.1例中的阻塞等待。当调用这个函数时我们传入上面定义的print回调句柄。
t.async_wait(print);
最后,我们必须在io_service对象上调用io_service::run()成员函数。
Asio保证回调句柄仅仅能被io_service::run()启动的当前线程所调用。 因此,如果io_service::run() 函数不执行,用于异步等待完成时的回调函数(在本例中为print函数)将永远不会被调用。
当仍旧有“工作”可做时,io_service::run() 函数会继续运行。在本例中,“工作”是定时器的异步等待,因此,直到定时器终止和回调函数执行完成,程序才会返回。
在调用io_service::run()之前确保给 io_service 一些工作去做,这非常重要。 例如,如果我们省略了上面调用的deadline_timer::async_wait() 函数,io_service对象将没有任何事情去做,因此io_service::run() 将立即返回。
io.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)
{
如上所示,示例程序使用了一个计数器,当定时器被第6次激活时,用来中止程序。然而,你将看到这里并没有显式地要求io_service对象中止。回忆示例2中,当没有更多“工作”去做时,io_service::run() 函数完成。在计数器达到 5时,定时器并没有启动一个新的异步等待。该io_service执行完工作后停止运行。
if (*count < 5)
{
std::cout << *count << "\n";
++(*count);
接着,我们推迟定时器的终止时间。通过在原先的终止时间上增加延时,我们可以确保定时器不会在处理回调函数所需时间内到期。
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中,如果在回调函数中,这个参数不是必需的,这个占位符会被省略。
t->async_wait(boost::bind(print,
boost::asio::placeholders::error, t, count));
}
}
int main()
{
boost::asio::io_service io;
为了在定时器第4次被激活时终止程序,我们添加一个新的count变量。
int count = 0;
boost::asio::deadline_timer t(io, boost::posix_time::seconds(1));
在第四步中,当在主函数中的调用deadline_timer::async_wait() 函数时,我们绑定print函数所需要的附加参数。
t.async_wait(boost::bind(print,
boost::asio::placeholders::error, &t, &count));
io.run();
最后,为了证明count 变量在print 函数句柄中被使用,我们打印出它的值。
std::cout << "Final count is " << count << "\n";
代码
#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 = 0
14d0
(等了5s)
count = 1
14d0
(等了5s)
count = 2
14d0
(等了5s)
Final count is 3
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(5))
,m_timer2(io, boost::posix_time::seconds(5))
,m_count(0)
{
当开始同步操作时,每一个回调句柄都使用boost::asio::strand对象进行“包装”。strand::wrap() 函数返回一个新的通过boost::asio::strand对象自动分派的内部句柄。 通过同一boost::asio::strand对象对句柄进行“ 包装”,我们可以保证操作不会并发执行。
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;
}
在一个多线程程序中,当访问同一共享资源时,异步操作必须是同步的。在本例中,print1 和print2)函数使用的共享资源std::cout 和count_数据成员。
void Print1()
{
if (m_count < 10)
{
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(1)) ;
m_timer1.async_wait(m_strand.wrap(boost::bind(&CPrinter::Print1, this) ) );
}
}
void Print2()
{
if (m_count < 10)
{
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(1)) ;
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;
};
main函数中, io_service::run() 现在被两个线程调用:主线程和一个附加线程。这一切依赖于boost::thread对象来完成。
正如它被一个单线程调用一样,io_service::run()的并发调用会一直持续到无任何“工作”可做。后台线程直到所有异步操作都完成后才会退出。
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 0;
}
代码
#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())
, timer2(ref_service, boost::posix_time::seconds())
, m_strand(ref_service)
, count()
{ 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 < )
{
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());
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 < )
{
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());
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 ;
}
运行结果:
异步给主线程运行到io_Service::run之前,知道异步回调函数调完
异步给主线程运行到io_Service::run之前,知道异步回调函数调完
进入t线程内部执行23d4 Print2's count :0
23d4
Pints1's count:1 Pints1's count:2 Print2's count :3
23d4
Print2's count :4
23d4
the Error is:操作成功完成。
the Error is:操作成功完成。
异步给主线程运行到io_Service::run之前,知道异步回调函数调完 (验证)!
t线程内部:异步给主线程运行到io_Service::run之前,知道异步回调函数调完 (验证)!
说明:
(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对象自动分派的内部句柄
注意事项:
异步等待时,程序会先执行主线程需要执行的操作,直到io_service::run之前的语句;当回调函数都执行完成之后,才继续执行io_service::run之后的操作,
有时你如果没给io_service::run做点事,run会立马返回,导致程序无响应,解决办法是添加boost::asio::io_service::work work(io_Service), work对象不析构,run是不会立即返回的!
work转载来自:http://blog.csdn.net/huang_xw/article/details/8471057
其中异步调用时,用boost::asio::strand,strand.wrap(boost::bind(....)),为了在函数共享数据时,使函数一缕一缕的执行,strand会内部打包函数,返回自己随机分配函数句炳!
还有,异步等待的时间要够回调函数的执行时间,否则会直接调用异步等待,所以你可以加入延时函数,deadline_timer::expires_at(.expires.at()+boost::posix_time::senconds(5));保证回调函数的执行时间;
本文转载自:http://blog.csdn.net/yockie/article/details/40386145
boost::asio::deadline_timer(理解)的更多相关文章
- boost.asio系列(一)——deadline_timer
一.构造函数 一个deadline_timer只维护一个超时时间,一个deadline_timer不同时维护多个定时器.在构造deadline_timer时指定时间: basic_deadline_t ...
- 概念理解:boost::asio::定时器2
多线程同步回调#include <cstdio> #include <iostream> #include <boost/asio.hpp> #include &l ...
- 概念理解:boost::asio::定时器1
同步定时器 #include <cstdio> #include <iostream> #include <boost/asio.hpp> #include < ...
- boost::asio译文
Christopher Kohlhoff Copyright © 2003-2012 Christopher M. Kohlhoff 以Boost1.0的软件授权进行发布(见附带的LICENS ...
- Boost.Asio技术文档
Christopher Kohlhoff Copyright © 2003-2012 Christopher M. Kohlhoff 以Boost1.0的软件授权进行发布(见附带的LICENSE_1_ ...
- boost Asio网络编程简介
:first-child { margin-top: 0px; } .markdown-preview:not([data-use-github-style]) h1, .markdown-previ ...
- Boost Asio介绍--之一
原文:http://www.tuicool.com/articles/YbeYR3 Boost Asio介绍--之一 时间 2014-03-26 17:57:39 CSDN博客 原文 http:/ ...
- Boost Asio(一)初探
一.简介 Boost Asio ( asynchronous input and output)关注数据的异步输入输出.Boost Asio 库提供了平台无关性的异步数据处理能力(当然它也支持同步数据 ...
- 如何在多线程leader-follower模式下正确的使用boost::asio。
#include <assert.h> #include <signal.h> #include <unistd.h> #include <iostream& ...
随机推荐
- Windows本地上传源码到Gitee远程仓库
1.下载Git,并安装. 安装时一路默认即可 https://git-scm.com/downloads 验证Git安装成功否 cmd 下输入,出现版本号即成功 git --version 2.生成s ...
- Java面试中的Spring方面问题
1.一般问题 1.1. 不同版本的 Spring Framework 有哪些主要功能? VersionFeatureSpring 2.5发布于 2007 年.这是第一个支持注解的版本.Spring 3 ...
- zip命令详解
基础命令学习目录首页 好文链接:https://www.cnblogs.com/yinzhengjie/p/6247833.html 原文链接:https://www.cnblogs.com/ferr ...
- Django_rest_framework_版本(待验证)
简介 API版本控制可以用来在不同的客户端使用不同的行为.REST框架提供了大量不同的版本设计. 版本控制是由传入的客户端请求决定的,并且可能基于请求URL,或者基于请求头. 有许多有效的方法达到版本 ...
- sprint2(第二天)
昨天没有想到餐桌的功能,今天加到展板.然后今天我们完成了餐桌模板,可以实现添加桌子的桌号.人数.修改和删除功能.不过由于今天学校网络不是很好,晚上我们clone了很久都没clone下来,所以没有上传代 ...
- js循环复制一个div
<html> <head> <title>Test of cloneNode Method</title> <script type=" ...
- bing词典vs有道词典对比测试报告——功能篇之细节与用户体验
之所以将细节与用户体验放在一起讨论,是因为两者是那么的密不可分.所谓“细节决定成败”,在细节上让用户感受方便.舒适.不费心而且温馨,多一些人文理念,多一些情怀,做出来的产品自然比其他呆板的产品更受欢迎 ...
- [2019BUAA软工]第0次代码作业
Visual Studio 单元测试的简单应用 写在前面 笔者根据作业的介绍以及Visual Studio 2017 文档的相关说明,进行了Visual Studio 单元测试的简单应用. Tip ...
- 实验1 熟悉Linux开发环境 实验报告
参见http://www.cnblogs.com/lhc-java/p/4970269.html
- sqlDataAdapter和SqlCommand的区别
因为DataSet是离线的,所以SqlDataAdapter这个对象是连接DataSet和数据库的桥梁,所有对DataSet的操作(填充,更新等)都要通过他 ado.net数据访问有两种方式: 1.离 ...