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 對象茫因。因此在主函數(shù)中我們做的第一件事就是聲明一個這個類型的對象:
boost::asio::io_service io;
接下來我們聲明一個boost::asio::deadline_timer類型的對象痰娱。作為 Asio的核心類,它提供的I/O功能(在此為定時器功能)通常用一個io_service 的引用作為其構(gòu)造函數(shù)的第一個參數(shù)箍铲。第二個參數(shù)設置一個從現(xiàn)在開始5秒后終止的定時器雇卷。
boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));
可以看一下boost::asio::deadline_timer的幾個構(gòu)造函數(shù)
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);
注意后兩種的區(qū)別,說明以下2種用法是等價的:
1 boost::asio::deadline_timer t(io, boost::posix_time::microsec_clock::universal_time()+boost::posix_time::seconds(5));
2 boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));
在這個簡單的程序中颠猴,我們用定時器演示一個阻塞等待关划。deadline_timer::wait() 函數(shù)調(diào)用直到定時器終止(從定時器被創(chuàng)建算起,五秒后終止)才會返回翘瓮。
一個deadline timer 通常是下面兩種狀態(tài)中的一種:"expired(終止)" 或"not expired(不終止)"贮折。如果deadline_timer::wait()
函數(shù)被一個已經(jīng)終止的定時器調(diào)用, 它將立即返回资盅。
t.wait();
最后我們打印出 "Hello, world!" 信息以顯示定時器已經(jīng)終止调榄。
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(5));
t.wait();
std::cout << "Hello, world!\n";
return 0;
}
2. Timer.2 - 使用異步定時器
本例使用Asio的異步回調(diào)功能在定時器中演示一個異步等待踊赠。
使用Asio的異步功能意味著當一個異步操作完成時一個回調(diào)函數(shù)將被調(diào)用。在本程序中我們定義一個名為print 的函數(shù)每庆,在異步等待結(jié)束后這個函數(shù)將被調(diào)用筐带。
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));
}
接下來,我們調(diào)用 deadline_timer::async_wait()
函數(shù)執(zhí)行一個異步等待去取代Timer.1例中的阻塞等待缤灵。當調(diào)用這個函數(shù)時我們傳入上面定義的print回調(diào)句柄伦籍。
t.async_wait(print);
最后,我們必須在io_service對象上調(diào)用io_service::run()
成員函數(shù)腮出。
Asio保證回調(diào)句柄僅僅能被io_service::run()啟動的當前線程所調(diào)用鸽斟。 因此桅狠,如果io_service::run() 函數(shù)不執(zhí)行厦凤,用于異步等待完成時的回調(diào)函數(shù)(在本例中為print函數(shù))將永遠不會被調(diào)用。
當仍舊有“工作”可做時尚镰,io_service::run()
函數(shù)會繼續(xù)運行慢逾。在本例中立倍,“工作”是定時器的異步等待,因此侣滩,直到定時器終止和回調(diào)函數(shù)執(zhí)行完成口注,程序才會返回。
在調(diào)用io_service::run()
之前確保給 io_service 一些工作去做君珠,這非常重要寝志。 例如,如果我們省略了上面調(diào)用的deadline_timer::async_wait()
函數(shù)策添,io_service對象將沒有任何事情去做材部,因此io_service::run()
將立即返回。
io.run();
和同步方式相比唯竹,它主要有兩點不同:
- 調(diào)用的是非阻塞函數(shù)async_wait乐导,它的入?yún)⑹且粋€回調(diào)函數(shù)。
- 顯式調(diào)用
io_service.run()
函數(shù)驅(qū)動異步IO調(diào)度浸颓。
值得提出的是物臂,異步回調(diào)函數(shù)handler的參數(shù)中有一個error,注意這個error很重要产上,表明這個handler是因為超時被執(zhí)行還是因為被cancel棵磷。
符合2種情況之一,handler被執(zhí)行:超時或者被cancel(可以通過此error的值進行區(qū)分)晋涣。
這同時隱含的說明了除非io.stop被調(diào)用仪媒,否則handler一定會被執(zhí)行。即便是被cancel姻僧。
被cancel有多種方法规丽,直接調(diào)用cancel或者調(diào)用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(5));
t.async_wait(Print);
cout<<"to run"<<endl;
io.run();
cout<<"exit"<<endl;
return 0;
}
3. 回調(diào)函數(shù)綁定參數(shù)
本例使定時器每秒被激活一次赌莺。例子將示范如何給你的函數(shù)指針傳遞附加參數(shù)冰抢。
使用Asio實現(xiàn)一個重復定時器,你必須在你的回調(diào)函數(shù)中去改變定時器的終止時間艘狭,然后開始一個新的異步等待挎扰。顯然這意味著回調(diào)函數(shù)必須擁有改變定時器對象的權(quán)限。為此我們?yōu)?print函數(shù)增加兩個新參數(shù):
- 一個指向定時器對象的指針巢音。
- 一個用于當定時器第6次被激活時我們可以中止程序的計數(shù)器遵倦。
void print(const boost::system::error_code& /*e*/,boost::asio::deadline_timer* t, int* count)
如上所示,示例程序使用了一個計數(shù)器,當定時器被第6次激活時官撼,用來中止程序梧躺。然而,你將看到這里并沒有顯式地要求io_service對象中止傲绣÷痈纾回憶示例2中,當沒有更多“工作”去做時秃诵,io_service::run() 函數(shù)完成续搀。在計數(shù)器達到 5時,定時器并沒有啟動一個新的異步等待菠净。該io_service執(zhí)行完工作后停止運行禁舷。
if (*count < 5)
{
std::cout << *count << "\n";
++(*count);
接著,我們推遲定時器的終止時間毅往。通過在原先的終止時間上增加延時牵咙,我們可以確保定時器不會在處理回調(diào)函數(shù)所需時間內(nèi)到期。
t->expires_at(t->expires_at() + boost::posix_time::seconds(1));
接著我們在定時器中啟動一個新的異步等待煞抬。我們必須使用boost::bind()
函數(shù)給你的回調(diào)函數(shù)綁定額外的參數(shù)霜大,因為deadline_timer::async_wait()
函數(shù)只期望得到一個擁用void(const boost::system::error_code&)
簽名的函數(shù)指針(或函數(shù)對象)构哺。為你的print函數(shù)綁定附加的參數(shù)后革答,它就成為與簽名精確匹配的函數(shù)對象。
在本例中曙强,boost::bind(
)的boost::asio::placeholders::error
參數(shù)是為了給回調(diào)函數(shù)傳入一個error對象残拐。當開始異步操作時,如果使用boost::bind()
碟嘴,你必須指定和回調(diào)函數(shù)的參數(shù)列表相匹配的一個參數(shù)溪食。在示例4中,如果在回調(diào)函數(shù)中娜扇,這個參數(shù)不是必需的错沃,這個占位符會被省略栅组。
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));
在第四步中玉掸,當在主函數(shù)中的調(diào)用deadline_timer::async_wait() 函數(shù)時,我們綁定print函數(shù)所需要的附加參數(shù)醒叁。
t.async_wait(boost::bind(print,
boost::asio::placeholders::error, &t, &count));
io.run();
最后司浪,為了證明count 變量在print 函數(shù)句柄中被使用,我們打印出它的值把沼。
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 < 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;
}
結(jié)果:
14d0
to run
(等了5s)
count = 0
14d0
(等了5s)
count = 1
14d0
(等了5s)
count = 2
14d0
(等了5s)
Final count is 3
exit
4. 多線程同步回調(diào)
本例示范了使用boost::asio::strand
類來創(chuàng)建多線程程序中的同步回調(diào)句柄啊易。
前幾個例程只是在單線程下使用io_service::run() 函數(shù)來避免處理函同步。 如你所見饮睬,Asio庫保證回調(diào)句柄僅能被當前正在調(diào)用 io_service::run(). 函數(shù)的線程調(diào)用租谈。 因此,在單線程中調(diào)用io_service::run() 能確崩Τ睿回調(diào)句柄不被并發(fā)運行垦垂。
下面是Asio在單線程程序中的局限性,尤其是服務器方面牙瓢,包括:
- 操作需要較長時間處理才能完成時弱響應劫拗。
- 在大規(guī)模的多處理機系統(tǒng)中表現(xiàn)不佳。
如果你發(fā)現(xiàn)自己陷入這些局限時矾克,一個可供選擇的方法是創(chuàng)建一個每個線程都調(diào)用io_service::run() 的線程池页慷。 不過,因為這允許并發(fā)操作胁附,當訪問一個共享酒繁、非線程安全的資源時,我們需要一個同步方式控妻。
讓我們從定義一個名為printer的類開始州袒,這與前一個示例中的類很相似。這個類是上一個例子的擴展弓候,這里我們使用兩個并行的定時器郎哭。
class CPrinter
{
除了初始化一對boost::asio::deadline_timer
成員變量外,構(gòu)造函數(shù)還初始化一個boost::asio::strand類型strand_
成員變量菇存。
boost::asio::strand
對象保證:對于通過它來分派執(zhí)行的眾操作中夸研,只有一個操作執(zhí)行完成之后才允許進入下一個操作。 這種保證與多少個線程調(diào)用io_service::run() 無關(guān)依鸥。當然亥至,如果不是通過一個boost::asio::strand
對象分派, 或者通過其它不同的boost::asio::strand
對象分派,這些操作仍舊可能是并發(fā)的姐扮。
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)
{
當開始同步操作時絮供,每一個回調(diào)句柄都使用boost::asio::strand
對象進行“包裝”。strand::wrap()
函數(shù)返回一個新的通過boost::asio::strand
對象自動分派的內(nèi)部句柄茶敏。 通過同一boost::asio::strand
對象對句柄進行“ 包裝”杯缺,我們可以保證操作不會并發(fā)執(zhí)行。
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)函數(shù)使用的共享資源std::cout 和count_數(shù)據(jù)成員塘揣。
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函數(shù)中,io_service::run()
現(xiàn)在被兩個線程調(diào)用:主線程和一個附加線程宿崭。這一切依賴于boost::thread對象來完成亲铡。
正如它被一個單線程調(diào)用一樣,io_service::run()的并發(fā)調(diào)用會一直持續(xù)到無任何“工作”可做葡兑。后臺線程直到所有異步操作都完成后才會退出奖蔓。
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(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之前,知道異步回調(diào)函數(shù)調(diào)完" << std::endl;
boost::thread t([&](){ std::cout << "\n進入t線程內(nèi)部執(zhí)行" << boost::this_thread::get_id() << std::endl;; Service.run();
std::cout << "t線程內(nèi)部:異步給主線程運行到io_Service::run之前讹堤,知道異步回調(diào)函數(shù)調(diào)完 (驗證)吆鹤!" << std::endl; });
t.detach();
std::cout << "異步給主線程運行到io_Service::run之前,知道異步回調(diào)函數(shù)調(diào)完" << std::endl;
Service.run();
std::cout << "異步給主線程運行到io_Service::run之前洲守,知道異步回調(diào)函數(shù)調(diào)完 (驗證)疑务!" << std::endl;
getchar();
return 0;
}
運行結(jié)果:
2544
異步給主線程運行到io_Service::run之前,知道異步回調(diào)函數(shù)調(diào)完
異步給主線程運行到io_Service::run之前梗醇,知道異步回調(diào)函數(shù)調(diào)完
進入t線程內(nèi)部執(zhí)行23d4
Print2'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之前叙谨,知道異步回調(diào)函數(shù)調(diào)完 (驗證)温鸽!
t線程內(nèi)部:異步給主線程運行到io_Service::run之前,知道異步回調(diào)函數(shù)調(diào)完 (驗證)手负!
說明:
兩個Timer確實是在不同線程中執(zhí)行涤垫,并且只有一個print操作執(zhí)行完成之后才允許進入另一個print操作
Timer1始終在一個線程中執(zhí)行,Timer2始終在另一個線程中執(zhí)行虫溜,(但不一定就是Timer1在主線程執(zhí)行雹姊,這個分配時隨機的)
注意:
deadline_timer和socket一樣,都用io_service作為構(gòu)造函數(shù)的參數(shù)衡楞。在其上進行異步操作,都將導致和io_service所包含的iocp相關(guān)聯(lián)。這同樣意味著在析構(gòu) io_service之前瘾境,必須析構(gòu)關(guān)聯(lián)在這個io_service上的deadline_timer歧杏。
引申:
關(guān)于boost::asio::strand,有三個函數(shù):post, dispatch, wrap
post: 不管什么情況都會把任務丟到隊列中迷守,然后立即返回犬绒。如:m_strand.post(boost::bind(print, 2));
dispatch: 如果跟run()在一個線程,那么任務會直接在dispatch內(nèi)部調(diào)用兑凿,執(zhí)行結(jié)束后返回凯力。不在一個線程跟post一樣。如:m_strand.dispatch(boost::bind(print, 1));
wrap:返回一個新的通過boost::asio::strand對象自動分派的內(nèi)部句柄
注意事項:
異步等待時礼华,程序會先執(zhí)行主線程需要執(zhí)行的操作咐鹤,直到io_service::run之前的語句;當回調(diào)函數(shù)都執(zhí)行完成之后圣絮,才繼續(xù)執(zhí)行io_service::run之后的操作祈惶,
有時你如果沒給io_service::run做點事,run會立馬返回扮匠,導致程序無響應捧请,解決辦法是添加boost::asio::io_service::work work(io_Service), work對象不析構(gòu),run是不會立即返回的棒搜!
work轉(zhuǎn)載來自:http://blog.csdn.net/huang_xw/article/details/8471057
其中異步調(diào)用時疹蛉,用boost::asio::strand,strand.wrap(boost::bind(....)),為了在函數(shù)共享數(shù)據(jù)時,使函數(shù)一縷一縷的執(zhí)行力麸,strand會內(nèi)部打包函數(shù)氧吐,返回自己隨機分配函數(shù)句炳!
還有末盔,異步等待的時間要夠回調(diào)函數(shù)的執(zhí)行時間筑舅,否則會直接調(diào)用異步等待,所以你可以加入延時函數(shù)陨舱,deadline_timer::expires_at(.expires.at()+boost::posix_time::senconds(5));保證回調(diào)函數(shù)的執(zhí)行時間翠拣;