[Boost基础]并发编程——asio网络库——定时器deadline_timer
asio庫基于操作系統提供的異步機制,采用前攝器設計模式(Proactor)實現了可移植的異步(或者同步)IO操作,而且并不要求使用多線程和鎖定,有些的避免了多線程編程帶來的諸多有害副作用(如條件競爭、死鎖等)。
目前asio主要關注網絡通信方面,使用大量的類和函數封裝了socket API,提供了一個現代C++風格的網絡編程接口,支持TCP,ICMP,UDP等網絡通信協議。但asio的異步操作并不局限于網絡編程,他還支持串口讀寫,定時器,SSL等功能,而且asio是一個很好的富有彈性的框架,可以擴展到其他有異步操作需要的領域。
asio概述
asio庫基于前攝模式(Proactor)封裝了操作系統的select,poll/epoll,kqueue,overlapped I/O等機制,實現了異步IO模型。他的核心類是io_service,相當于前攝模式中的Proactor角色,asio的任何操作都需要有io_service的參與。
在同步模式下,程序發起一個IO操作,向io_service提交請求,io_service把操作轉給操作系統,同步的等待。當IO操作完成時,操作系統通知io_service,然后io_service在把結果發回給程序,完成整個同步流程。這個處理流程與多線程join()等待方式很相似。
在異步模式下,程序除了要發起的IO操作,還要定義一個用于回調的完成處理函數。io_service同樣把IO操作轉交給操作系統執行,但它不同步等待,而是立即返回。調用io_service的run()成員函數可以等待異步操作完成,當異步操作完成時io_service從操作系統獲取執行結果,調用完成處理函數。
asio不直接使用操作系統提供的線程,而且定義了一個自己的線程概念:strand,它保證在多線程的環境中代碼可以正確的執行,而無需使用互斥量。io_serviec::strand::wrap()函數可以包裝在一個函數在strand中執行。
IO操作會經常使用到緩沖區,asio庫專門用兩個類mutable_buffer和const_buffer來封裝這個概念,他們可以被安全的應用在異步的讀寫操作中,使用自由函數buffer()能夠包裝常用的C++容器類型,如數組、arrary、vector、string等,用read(),write()函數來讀取緩沖區。
asio是一個相當復雜的庫,本書只涉及其中的基本概念,還有許多異步IO相關的概念,在此不一一介紹。
定時器
定時器是asio庫里最簡單的一個IO模型示范,提供等待時間終止的功能,通過它我們可以快速熟悉asio的基本使用方法。?
?
#include <iostream> #include <conio.h> using namespace std; #include <boost/asio.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/function.hpp> #include <boost/bind.hpp> using namespace boost; using namespace boost::asio; //同步定時器 void test1() {io_service ios;//所有的asio程序必須要有一個io_service對象deadline_timer t(ios,boost::posix_time::seconds(2));//兩秒鐘后定時器終止cout<<t.expires_at()<<endl;//查看終止的決定時間t.wait();//調用wait()同步等待cout<<"hello asio"<<endl; //可以把它與thread庫的sleep()函數對比研究一下,兩種雖然都是等待,但內部機制完全不同:thread庫的sleep()使用了互斥量和條件變量,在線程中等待,而asio則是調用了操作系統的異步機制,如select,epool等完成。 //同步定時器的用法很簡單,但它演示了asio程序的基本結構和流程:一個asio程序首先要定義一個io_service對象,它是前攝器模式中最重的proactor角色,然后我們聲明一個IO操作(這里是定時器),并把它掛接在io_service上,再然后就可以執行后續的 同步或異步操作。 } //異步定時器:代碼大致與同步定時器相同,增加了回調函數,并使用io_service_run()和定時器的async_wait() //首先我們要定義回調函數,asio庫要求回調函數只能有一個參數,而且這個參數必須是const asio::error_code&類型void myprint(boost::system::error_code ec) { cout<<"hello asio"<<endl; } void test2() {io_service ios;deadline_timer t(ios,boost::posix_time::seconds(3));t.async_wait(myprint);//異步等待,傳入回調函數,立即返回cout<<"it show before t expired."<<endl;ios.run();//很重要,異步IO必須cout<<"runned"<<endl;//將與hello asio一起輸出,說明run()是阻塞函數//異步等待async_wait(),它通知io_service異步的執行IO操作,并且注冊了回調哈思楠,用于在IO操作完成時有事件多路分離器分派返回值(error_code)調用。//最后我們必須調用io_service的run()成員函數,它啟動前攝器的事件處理循環,阻塞等待所有的操作完成并分派事件。如果不調用run()那么雖然被異步執行了,但沒有一個等待他完成的機制,回調函數將得不到執行的機會。 } //下面我們實現一個可以定時執行任意函數的定時器 a_timer(asyc timer),它持有一個asio定時器對象和一個計數器,還有一個function對象用來保存回調函數 class a_timer { private:int count,cout_max; //計數器成員變量function<void()> f; //function對象,持有無參數無返回的可調用物deadline_timer t;//asio定時器 public://構造函數初始化成員變量,將計數器清理,設置計數器的上限,拷貝存儲回調函數,并立即啟動定時器//之所以要"立即"啟動,是因為我們必須包裝在io_service.run()之前至少有一個異步操作在執行,否則io_service.run()會因為沒有事件處理而立即不等待返回。template<typename F>a_timer(io_service& ios,int x,F func):f(func),cout_max(x),count(0),t(ios,posix_time::microsec(500))//模板類型,可接受任意可調用物{t.async_wait(bind(&a_timer::call_func,this,placeholders::error));//命名空間下asio::placeholders的一個占位符error,他的作用類似于bind庫的占位符_1,_2,用于傳遞errror_code值。}//call_func()內部累加計數器,如果計數器未達到上限則調用function對象f,然后重新設置定時器的終止時間,再次異步等待被調用,從而達到反復執行的目的。void call_func(const boost::system::error_code &){if(count >= cout_max){return;}++count;f();t.expires_at(t.expires_at()+posix_time::millisec(500));//設置定時器的終止時間為0.5秒之后t.async_wait(bind(&a_timer::call_func,this,placeholders::error));} }; void print1(int index) {cout<<"hello asio:"<<index<<endl; } void print2() {cout<<"hello boost"<<endl; } void test3() {io_service ios; a_timer a(ios,10,bind(print1,2));//啟用第一個定時器a_timer at(ios,5,print2);//啟用第二個定時器ios.run();//ios等待異步調用結束 } //查詢網絡地址 /*之前關于TCP通信的所有論述我們都是使用直接的IP地址,但在實際生活中大多數時候我們不可能知道socket連接另一端的地址,而只有一個域名,這時候我們就需要使用resolver類來通過域名獲得可用的IP,它可用實現與IP版本無關的網址解析。 resolver使用內部類query和iterator共同完成查詢IP地址的工作:首先使用網址和服務器名創建query對象,然后由resolver()函數生產iterator對象,它代表了查詢到的ip端點。之后就可以使用socket對象嘗試連接,直到找到一個可用的為止。 */ #include <boost/lexical_cast.hpp> void resolv_connect(ip::tcp::socket &sock,const char* website ,int port)//使用此函數來封裝resolver的調用過程 {ip::tcp::resolver rlv(sock.get_io_service());//resolver對象ip::tcp::resolver::query qry(website,lexical_cast<string>(port));//創建一個query對象;query只接受字符串參數ip::tcp::resolver::iterator iter=rlv.resolve(qry);ip::tcp::resolver::iterator end;//逾尾迭代器system::error_code ec=error::host_not_found;for (; ec && iter != end;++iter){sock.close();sock.connect(*iter,ec);//嘗試連接端點}if (ec)//只有當ec為0時,才會連接成功{cout<<"can't connect."<<endl;throw system::system_error(ec);}cout<<"connect success."<<endl; } void test4() {try{io_service ios;ip::tcp::socket sock(ios);resolv_connect(sock,"www.boost.org",80);//其他操作ios.run();} catch (std::exception& e){cout<<e.what()<<endl;} } void test(char t) { std::cout<<"press key====="<<t<<std::endl; switch (t) { case '1':test1();break; case '2':test2();break; case '3':test3();break; case '4':test4();break; case 27: case 'q':exit(0);break; default: std::cout<<"default "<<t<<std::endl;break; } } void main() { while(1) test(getch()); }?
?
轉載于:https://www.cnblogs.com/pangblog/p/3290178.html
總結
以上是生活随笔為你收集整理的[Boost基础]并发编程——asio网络库——定时器deadline_timer的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 去除VisualStudio中拼写错误检
- 下一篇: LeetCode-Balanced Bi