服務(wù)器兩種高效的并發(fā)模式

一、并發(fā)編程與并發(fā)模式

并發(fā)編程主要是為了讓程序同時(shí)執(zhí)行多個(gè)任務(wù),并發(fā)編程對(duì)計(jì)算精密型沒有優(yōu)勢(shì),反而由于任務(wù)的切換使得效率變低。如果程序是IO精密型的,則由于IO操作遠(yuǎn)沒有CPU的計(jì)算速度快,所以讓程序阻塞于IO操作將浪費(fèi)大量的CPU時(shí)間。如果程序有多個(gè)線程,則當(dāng)前被IO操作阻塞的線程可主動(dòng)放棄CPU,將執(zhí)行權(quán)轉(zhuǎn)給其它線程。

IO精密型和cpu精密型可以參考此文:CPU-bound(計(jì)算密集型) 和I/O bound(I/O密集型)

并發(fā)編程主要有多線程和多進(jìn)程,這里我們先討論并發(fā)模式,并發(fā)模式指:IO處理單元和多個(gè)邏輯直接協(xié)調(diào)完成任務(wù)的方法。服務(wù)器主要有兩種并發(fā)編程模式:

  • 半同步/半異步模式(half-sync/half-async)
  • 領(lǐng)導(dǎo)者/追隨者模式(Leader/Followers)

二、半同步/半異步模式(half-sync/half-async

這里的“同步”和“異步”和“IO”的“同步”“異步”是完全不同的概念。在IO模型中,“同步”和“異步”區(qū)分的是內(nèi)核向應(yīng)用程序通知的是何種IO事件(是就緒事件還是完成事件),以及該由誰(shuí)來(lái)完成IO讀寫(是應(yīng)用程序還是內(nèi)核)。在并發(fā)模式中,“同步”指的是程序完全按照代碼序列的順序執(zhí)行;“異步”指的是程序的執(zhí)行需要由系統(tǒng)事件來(lái)驅(qū)動(dòng)。常見的系統(tǒng)事件包括中斷、信號(hào)等。

下圖1描述了并發(fā)模式同步讀操作(圖1a)和異步讀操作(圖1b)

圖1并發(fā)模式同步讀(a)和異步讀(b)

已同步方式運(yùn)行的線程為同步線程,異步方式運(yùn)行的為異步線性,異步線程的執(zhí)行效率高,實(shí)時(shí)性強(qiáng),但編寫異步方式執(zhí)行的程序相對(duì)復(fù)雜,難于調(diào)試和擴(kuò)展,而且不適合于大量的并發(fā)。同步線程則相反,它雖然效率相對(duì)較低,實(shí)時(shí)性較差,但邏輯簡(jiǎn)單。

因此對(duì)應(yīng)服務(wù)器要求實(shí)時(shí)性及同時(shí)處理多個(gè)請(qǐng)求的程序,可以同時(shí)使用同步線程和異步線程即采用半同步/半異步模式。同步線程用于處理客戶邏輯,異步線程用于處理IO事件。異步線程監(jiān)聽到客戶請(qǐng)求后,就將其封裝成請(qǐng)求對(duì)象并插入到請(qǐng)求隊(duì)列中。請(qǐng)求隊(duì)列將通知某個(gè)工作在同步模式的工作線程來(lái)讀取并處理該請(qǐng)求對(duì)象。具體哪個(gè)線性處理取決于請(qǐng)求隊(duì)列的設(shè)計(jì)。下圖2為半同步/半異步的工作流程


圖2半同步/半異步的工作流程

在半同步/半異步模式可以變體成為半同步/半反應(yīng)堆(half-sync/half-reactive),如下圖3

圖3半同步/半反應(yīng)堆模式

半同步/半反應(yīng)堆中,異步線程只有一個(gè),即主線程,他負(fù)責(zé)監(jiān)聽所有事件,有事件發(fā)生則將事件插入請(qǐng)求隊(duì)列中。工作線程休眠在請(qǐng)求隊(duì)列中,當(dāng)任務(wù)到來(lái)時(shí),通過(guò)競(jìng)爭(zhēng)獲取任務(wù)處理權(quán)。

在上圖3半同步/半反應(yīng)堆中,主線程插入工作隊(duì)列的為就緒的連接socket,他要求工作線程自己socket讀取數(shù)據(jù)和往socket寫入服務(wù)器應(yīng)答,所有可以看作Reactor模式。實(shí)際也可以模擬為Proactor模式,即主線程完成數(shù)據(jù)的讀寫,將數(shù)據(jù)封裝成任務(wù)對(duì)象插入請(qǐng)求隊(duì)列,工作線程從請(qǐng)求隊(duì)列取出任務(wù)對(duì)象處理。(Reactor模式和Reactor模式可以參考此文:服務(wù)器兩種高效的事件處理模式

半同步半反應(yīng)堆模式存在如下缺點(diǎn):

  1. 主線程和工作線程共享請(qǐng)求隊(duì)列,對(duì)請(qǐng)求隊(duì)列的操作需求加鎖,耗費(fèi)CPU時(shí)間。
  2. 每一個(gè)工作線程在同一時(shí)間只能處理一個(gè)客戶請(qǐng)求??蛻魯?shù)量多,工作線程少,請(qǐng)求隊(duì)列任務(wù)堆積,響應(yīng)滿,如果添加試圖通過(guò)增加線程則,由于線程切換導(dǎo)致的CPU時(shí)間消耗。
    這里我們?cè)俳榻B一種高效的半同步/半異步模式:每個(gè)工作線程都能同時(shí)處理多個(gè)客戶連接。
圖4 高效半同步/半異步模式

主線程只管理監(jiān)聽socket,連接socket由工作線程來(lái)管理。當(dāng)有新的連接到來(lái)時(shí),主線程就接受之并將新返回的連接socket派發(fā)給某個(gè)工作線程,此后該socket上的任何IO操作都由被選中的工作線程來(lái)處理,直到客戶端關(guān)閉連接。主線程向工作線程派發(fā)socket的最簡(jiǎn)單的方式,是往它和工作線程之間的管道里寫數(shù)據(jù)。工作線程檢測(cè)到管道里有數(shù)據(jù)可讀時(shí),就分析是否是一個(gè)新的客戶連接請(qǐng)求到來(lái)。如果是,則把該新socket上的讀寫事件注冊(cè)到自己的epoll內(nèi)核事件表中。每個(gè)線程(主線程和工作線程)都維持自己的事件循環(huán),它們各自獨(dú)立的監(jiān)聽不同的事件。因此在這種模式中,每個(gè)線程都工作在異步模式,所以它并非嚴(yán)格意義上的半同步半異步模式。

三、領(lǐng)導(dǎo)者/追隨者模式(Leader/Followers

領(lǐng)導(dǎo)者/追隨者模式是多個(gè)工作線程輪流獲得事件源集合,輪流監(jiān)聽、分發(fā)并處理事件的一種模式。在任意時(shí)間點(diǎn),程序都僅有一個(gè)領(lǐng)導(dǎo)者線程,它負(fù)責(zé)監(jiān)聽I(yíng)O事件。而其他線程都是追隨者,它們休眠在線程池中等待成為新的領(lǐng)導(dǎo)者。當(dāng)前的領(lǐng)導(dǎo)者如果檢測(cè)到IO事件,首先要從線程池中推選出新的領(lǐng)導(dǎo)者線程,然后處理IO事件。此時(shí),新的領(lǐng)導(dǎo)者等待新的IO事件,而原來(lái)的領(lǐng)導(dǎo)者則處理IO事件,二者實(shí)現(xiàn)了并發(fā)。包含如下幾個(gè)組件:

  • 句柄集(HandleSet)
  • 線程集(ThreadSet)
  • 事件處理器(EventHandler)
  • 具體的事件處理器(ConcreteEventHandler)。

關(guān)系如下圖5


圖5 領(lǐng)導(dǎo)者/追隨者模式的組件

1、句柄集
句柄表示IO資源,linux下通常是文件描述符。句柄集使用wait_for_event方法監(jiān)聽這些句柄上的IO事件,并將其中的就緒事件通知給領(lǐng)導(dǎo)者線程。領(lǐng)導(dǎo)者調(diào)用綁定到Handle上的事件處理器來(lái)處理事件。綁定是通過(guò)句柄集的register_handle方法實(shí)現(xiàn)的。

2、線程集
所有工作線程的管理者,負(fù)責(zé)線程同步、推選新領(lǐng)導(dǎo)。線程在任一時(shí)間必處于以下三種狀態(tài)之一:

  • Leader:領(lǐng)導(dǎo)者線程,負(fù)責(zé)等待句柄集上的IO事件。
  • Processing:線程正在處理事件。領(lǐng)導(dǎo)者檢測(cè)到IO事件后可以轉(zhuǎn)移至Processing狀態(tài)處理該事件,并調(diào)用promote_new_leader方法推選新領(lǐng)導(dǎo)者;也可以指定其他追隨者來(lái)處理事件,此時(shí)領(lǐng)導(dǎo)者地位不變。當(dāng)處于Processing狀態(tài)的線程處理完事件后,如果當(dāng)前線程集中沒有領(lǐng)導(dǎo)者,則它將成為新領(lǐng)導(dǎo)者,否則它直接轉(zhuǎn)為追隨者。
  • Follower:線程處于追隨者身份,通過(guò)調(diào)用線程集的join方法等待成為新領(lǐng)導(dǎo)者,也可能被領(lǐng)導(dǎo)者指定來(lái)處理新的事件。

這三種狀態(tài)之間的轉(zhuǎn)換關(guān)系圖如下圖6:

圖6 領(lǐng)導(dǎo)者/追隨者模式的狀態(tài)轉(zhuǎn)移

(注意,領(lǐng)導(dǎo)者推選新領(lǐng)導(dǎo)和追隨者等待成為新領(lǐng)導(dǎo)這兩個(gè)操作都會(huì)修改線程集,因此線程集提供一個(gè)Synchronizer來(lái)同步。)

3、事件處理器和具體的事件處理器
事件處理器通常包含一個(gè)或多個(gè)回調(diào)函數(shù)handle_event。這些回調(diào)函數(shù)用于處理事件對(duì)應(yīng)的業(yè)務(wù)邏輯。事件處理器在使用前需要被綁定到某個(gè)句柄上,當(dāng)該句柄有事件發(fā)生時(shí),領(lǐng)導(dǎo)者就執(zhí)行綁定的事件處理器的回調(diào)函數(shù)。具體的事件處理器是事件處理器的派生類。它們重新實(shí)現(xiàn)基類的handle_event方法,以處理特定的任務(wù)。

由于領(lǐng)導(dǎo)者自己監(jiān)聽I(yíng)O事件并處理客戶請(qǐng)求,該模式不需要在線程間傳遞額外數(shù)據(jù),也無(wú)需像半同步/半反應(yīng)堆模式那樣在線程間同步對(duì)請(qǐng)求隊(duì)列的訪問(wèn)。但是,該模式的明顯缺點(diǎn)是僅支持一個(gè)事件源集合,因此也無(wú)法讓每個(gè)工作線程獨(dú)立管理多個(gè)客戶連接。
我們將領(lǐng)導(dǎo)者/追隨者模式的工作流程總結(jié)如下圖7

圖7 領(lǐng)導(dǎo)者/追隨者模式的工作流程

注(本文內(nèi)容參考 Linux高性能服務(wù)器編程——第八章 游雙著)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,175評(píng)論 25 708
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,644評(píng)論 19 139
  • 接上一篇《那些山,那些水【Part I】》,依然是六月份西藏旅拍的照片,每周發(fā)布9張,大部分后期是由@山里囤 完成...
    不許瞎搞閱讀 678評(píng)論 0 0
  • 要如何才能將記憶里那些過(guò)去生活的片段拿出來(lái),湊成生活本來(lái)的樣子?或許永遠(yuǎn)都不可能!我們一路走著,今天比明天聰明一點(diǎn)...
    走魚閱讀 261評(píng)論 0 3
  • 賈寶玉林黛玉薛寶釵名字的同與異 在《紅樓夢(mèng)》中,寫了許許多多的人物,寫了大大小小的事情,描寫了四大家族由盛轉(zhuǎn)哀的...
    舟?閱讀 1,407評(píng)論 7 12

友情鏈接更多精彩內(nèi)容