boost------asio庫的使用1(Boost程式庫完全開發指南)讀書筆記
asio庫基於作業系統提供的非同步機制,採用前攝器設計模式(Proactor)實現了可移植的非同步(或者同步)IO操作,而且並不要求多執行緒和鎖定,有效地避免了多執行緒程式設計帶來的諸多有害副作用。
目前asio主要關注於網路通訊方面,使用大量的類和函式封裝了socket API,支援TCP、TCMP、UDP等網路通訊協議。但asio的非同步操作並不侷限於網路程式設計,它還支援串列埠讀寫、定時器、SSL等功能,而且asio是一個很好的富有彈性的框架,可以擴充套件到其他有非同步操作需要的領域
概述
asio庫基於前攝器模式封裝了作業系統的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_service::strand::wrap()函式可以包裝一個函式在strand中執行。
asio庫使用system庫的error_code和system_error來表示程式執行的錯誤。
定時器
定時器是asio庫裡最簡單的一個IO模型示範,提供等待時間終止的功能,通過它我們可以快速熟悉asio的基本使用方法:
同步定時器
#include "stdafx.h" #include "boost/asio.hpp" #include "boost/date_time/posix_time/posix_time.hpp" #include "iostream" using namespace std; int _tmain(int argc, _TCHAR* argv[]) { boost::asio::io_service ios; // 所以的asio程式必須要有一個io_service物件 // 定時器io_service作為建構函式引數,兩秒鐘之後定時器終止 boost::asio::deadline_timer t(ios, boost::posix_time::seconds(2)); cout << t.expires_at() << endl; // 檢視終止的絕對事件 t.wait(); // 呼叫wait同步等待 cout << "hello asio" << endl; return 0; }
可以把它與thread庫的sleep()函式對比研究一下,兩者都是等待,但內部機制完成不同:thread庫的sleep()使用了互斥量和條件變數,線上程中等待,而asio則是呼叫了作業系統的非同步機制,如select、epoll等完成的。
非同步定時器
下面的是非同步定時器,程式碼大致與同步定時器相等,增加了回撥函式,並使用io_service.run()和定時器的async_wait()方法
#include "stdafx.h"
#include "boost/asio.hpp"
#include "boost/date_time/posix_time/posix_time.hpp"
#include "iostream"
using namespace std;
void Print(const boost::system::error_code& error)
{
cout << "hello asio" << endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
boost::asio::io_service ios; // 所以的asio程式必須要有一個io_service物件
// 定時器io_service作為建構函式引數,兩秒鐘之後定時器終止
boost::asio::deadline_timer t(ios, boost::posix_time::seconds(2));
t.async_wait(Print); // 呼叫wait非同步等待
cout << "it show before t expired." << endl;
ios.run();
return 0;
}
程式碼的前兩行與同步定時器相同,這是所有asio程式基本的部分。重要的是非同步等待async_wait(),它通知io_service非同步地執行io操作,並且註冊了回撥函式,用於在io操作完成時由事件多路分離器分派返回值(error_code)呼叫
最後必須呼叫io_service的run()成員函式,它啟動前攝器的事件處理迴圈,阻塞等待所有的操作完成並分派事件。如果不呼叫run()那麼雖然操作被非同步執行了,但沒有一個等待它完成的機制,回撥函式將得不到執行機會。
非同步定時器使用bind
非同步定時器中由於引入了回撥函式,因此產生了很多的變化,可以增加回調函式的引數,使它能夠做更多的事情。但async_wait()接受的回撥函式型別是固定的,必須使用bind庫來繫結引數以適配它的介面
下面實現一個可以定時執行任意函式的定時器AsynTimer(asyctimer),它持有一個asio定時器物件和一個計數器,還有一個function物件來儲存回撥函式
#include "stdafx.h"
#include "boost/asio.hpp"
#include "boost/date_time/posix_time/posix_time.hpp"
#include "boost/bind.hpp"
#include "boost/function.hpp"
#include "iostream"
using namespace std;
class AsynTimer
{
public:
template<typename F> // 模板型別,可以接受任意可呼叫物
AsynTimer(boost::asio::io_service& ios, int x, F func)
:f(func), count_max(x), count(0), // 初始化回撥函式和計數器
t(ios, boost::posix_time::millisec(500)) // 啟動計時器
{
t.async_wait(boost::bind(&AsynTimer::CallBack, // 非同步等待計時器
this, boost::asio::placeholders::error)); // 註冊回撥函式
}
void CallBack(const boost::system::error_code& error)
{
if (count >= count_max) // 如果計數器達到上限則返回
{
return;
}
++count;
f(); // 呼叫function物件
// 設定定時器的終止時間為0.5秒之後
t.expires_at(t.expires_at() + boost::posix_time::microsec(500));
// 再次啟動定時器,非同步等待
t.async_wait(boost::bind(&AsynTimer::CallBack, this, boost::asio::placeholders::error));
}
private:
int count;
int count_max;
boost::function<void()> f; // function物件,持有無參無返回值的可呼叫物
boost::asio::deadline_timer t; // asio定時器物件
};
// 第一個回撥函式
void print1()
{
cout << "hello asio" << endl;
}
// 第二個回撥函式
void print2()
{
cout << "hello boost" << endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
boost::asio::io_service ios;
AsynTimer t1(ios, 10, print1); // 啟動第一個定時器
AsynTimer t2(ios, 10, print2); // 啟動第二個定時器
ios.run(); // io_service等待非同步呼叫結束
return 0;
}
注意在async_wait()中bind的用法,CallBack是AsynTimer的一個成員函式,因此需要繫結this指標,同時還使用了asio下子名字空間placeholders下的一個佔位符error,他的作業類似於bind庫的佔位符_1、_2,用於傳遞error_code值。
接下來是AsynTimer的主要功能函式CallBack,它符合async_wait()對回撥函式的要求,有一個error_code引數,當定時器終止時它將被呼叫執行
CallBack函式內部累加器,如果計數器未達到上限則呼叫function物件f,然後重新設定定時器的終止時間,再次非同步等待被呼叫,從而達到反覆執行的目的