首頁  >  文章  >  Java  >  boost::io_service解讀

boost::io_service解讀

坏嘻嘻
坏嘻嘻原創
2018-09-14 09:32:171974瀏覽

io_servie 實作了一個任務佇列,這裡的任務就是void(void)的函數。 Io_servie最常用的兩個介面是post和run,post向任務佇列中投遞任務,run是執行佇列中的任務,直到全部執行完畢,並且run可以被N個執行緒呼叫。 Io_service是完全線程安全的佇列。

boost::io_service解讀

asio是boost提供的一個c 非同步程式設計模型庫,其核心類別io_service,在多執行緒程式設計裡面提供了任務佇列和任務分發功能,在socket 、io程式設計裡主要作為一個事件磁碟機(完成連接埠、select、poll、epoll等)。

佇列模型

每個io_service都一個公有任務佇列,和多個私有任務佇列,公有佇列由各個執行緒共用,私有佇列則是每個執行緒獨享一個。

boost::io_service解讀

io_service的任務執行流程大致如下:

  1. #呼叫run方法,進入主loop;

  2. 判斷公有佇列是否為空,不為空則取出任務並執行,當任務數大於1時同時喚醒其他空閒執行緒;

  3. 任務執行結束,把各個執行緒的私有隊伍裡面的任務移動到公有任務佇列裡面;

  4. 觸發reactor,linux下面一般是epoll,當有事件時,把對應的事件的任務放到私有隊列裡。

  5. 當佇列為空時,把目前執行緒加到空閒執行緒佇列裡面,同時進入wait狀態,等待其他執行緒的喚醒(task_operation)。

  6. 當使用者呼叫post時,任務是直接投遞到公有佇列op_queue裡面。

執行緒池模型

常用的執行緒池模型有兩種:

一種是多個執行緒共用一個任務佇列,使用者把任務投遞到該任務佇列中,其他執行緒競爭從該佇列中取得任務執行。結合boost::thread,在多個執行緒裡面呼叫run方法,即可實作該執行緒池:

using namespace boost;
using namespace boost::asio;
io_service ios;
int thread_num = 10;
thread *t[thread_num] = {0};
// 创建线程池
for(int i = 0; i < thread_num; ++i)
{
    t[i] = new thread(bind(&io_service::run, &ios));
}

// 向任务队列中投递任务,该任务将随机由一个调用run方法的线程执行
ios.post(func);

// 等待线程退出
for(int i = 0; i < thread_num; ++i)
{
    t[i]->join();
}

很容易看出來,這種執行緒池的瓶頸在於一個任務佇列,多個執行緒競爭取任務,在大並發的程式裡面容易導致效能下降。

另一種是每個執行緒各自維護一個任務佇列,使用者可以選擇隨機或輪訓地投遞任務到其中一個任務佇列裡面,該任務佇列中的任務只由其所在的執行緒才能消費。這種執行緒池在boost的example裡面也有對應的實作(io_service_pool),基本方法是建立多個io_service對象,每個對應一個thread,程式碼如下:

 

using namespace boost;
using namespace boost::asio;
int thread_num = 10;
io_service ios[thread_num];
thread *t[thread_num] = {0};
// 创建线程池
for(int i = 0; i < thread_num; ++i)
{
    t[i] = new thread(bind(&io_service::run, &ios[i]));
}

// 轮训投递任务
for(int i = 0; i < thread_num; ++i)
{
    ios[i].post(func);
}   

// 等待线程退出
for(int i = 0; i < thread_num; ++i)
{
    t[i]->join();
}

以下是基於linux環境作的類別圖,因為在windows上,有些類別有不同的定義。

boost::io_service解讀

io_service定義了主要的接口,在linux下的實作是task_io_service。

task_io_service主要定義了三個東西:

  1. 一個reactor,reactor就是完成連接埠、select、poll、epoll之類的事件磁碟機;

  2. 一個公共的任務佇列op_queue,用來存放使用者post的任務,和reactor傳回的任務;

  3. 執行緒相關變數。 io_service本身不會建立任何線程,但它會保存一些線程呼叫訊息,例如線程私有隊列等。

此外,task_io_service也維護了一個空閒執行緒列表,在有多餘任務到來時,喚醒其中一個空閒執行緒。在常見的linux單任務佇列的線程池裡面,用一個condition變數來喚醒線程,在多核心系統裡面,一次pthread_cond_signal調用,會喚起處於wait狀態的一個或多個線程(參考https://linux.die. net/man/3/pthread_cond_signal),儘管只有一個任務,如果採用空閒線程的方法,有任務時只喚醒一個空閒線程,可以減少很多不必要喚醒。

thread_info_base類別維護了一個簡單的記憶體池,只有一塊內存,僅在連續的申請釋放記憶體情況下,可以減少記憶體開啟的開銷。

io_service::work的作用:io_service::run在任務執行完畢時會立刻返回,這並不是寫常駐服務的程式所想要的,boost給的解決方案是定義一個work變量,乍看很神奇,這個跟io_server似乎沒有任何關係的變數竟然能控制run的行為,打開源碼一看,work的實作簡單的出奇,它只是在建構函式裡呼叫io_service的worked_started()方法,使其待處理任務數( outstanding_work_)大於0,如此,io_service::run就認為一直有任務待處理而不回傳了。

相關建議:

Angular 根據 service 的狀態更新 directive_AngularJS

簡介AngularJS中使用factory和service的方法_AngularJS

#

以上是boost::io_service解讀的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn