boost------asio庫的使用1(Boost程式庫完全開發指南)讀書筆記

孤獨的糖三角發表於2013-08-01

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,然後重新設定定時器的終止時間,再次非同步等待被呼叫,從而達到反覆執行的目的

相關文章