歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

C++ 協程與網絡編程

協程

協程,即協作式程序,其思想是,一系列互相依賴的協程間依次使用CPU,每次只有一個協程工作,而其他協程處於休眠狀態。協程可以在運行期間的某個點上暫停執行,並在恢復運行時從暫停的點上繼續執行。 協程已經被證明是一種非常有用的程序組件,不僅被python、lua、ruby等腳本語言廣泛采用,而且被新一代面向多核的編程語言如golang rust-lang等采用作為並發的基本單位。 協程可以被認為是一種用戶空間線程,與傳統的線程相比,有2個主要的優點:

  • 與線程不同,協程是自己主動讓出CPU,並交付他期望的下一個協程運行,而不是在任何時候都有可能被系統調度打斷。因此協程的使用更加清晰易懂,並且多數情況下不需要鎖機制。
  • 與線程相比,協程的切換由程序控制,發生在用戶空間而非內核空間,因此切換的代價非常小。

網絡編程模型

首先來簡單回顧一下一些常用的網絡編程模型。網絡編程模型可以大體的分為同步模型和異步模型兩類。

  • 同步模型:

同步模型使用阻塞IO模式,在阻塞IO模式下調用read等IO函數時會阻塞線程直到IO完成或失敗。

同步模型的典型代表是thread per connection模型,每當阻塞在主線程上的accept調用返回時則創建一個新的線程去服務於新的socket的讀/寫。這種模型的優點是程序簡潔,編寫簡單;缺點是可伸縮性收到線程數的限制,當連接越來越多時,線程也越來越多,頻繁的線程切換會嚴重拖累性能。

  • 異步模型:

異步模型一般使用非阻塞IO模式,並配合epoll/select/poll等多路復用機制。在非阻塞模式下調用read,如果沒有數據可讀則立即返回並通知用戶沒有可讀(EAGAIN/EWOULDBLOCK),而非阻塞當前線程。異步模型可以使一個線程同時服務於多個IO對象。

異步模型的典型代表是reactor模型。在reactor模型中,我們將所有要處理的IO事件注冊到一個中心的IO多路復用器中(一般為epoll/select/poll),同時主線程阻塞在多路復用器上。一旦有IO事件到來或者就緒,多路復用器返回並將對應的IO事件分發到對應的處理器(即回調函數)中,最後處理器調用read/write函數來進行IO操作。

異步模型的特點是性能和可伸縮性比同步模型要好很多,但是其結構復雜,不易於編寫和維護。在異步模型中,IO之前的代碼(IO任務的提交者)和IO之後的處理代碼(回調函數)是割裂開來的。

協程與網絡編程

協程為克服同步模型和異步模型的缺點,並結合他們的優點提供了可能:

首先簡單認識一下調度器:一個線程運行一個調度器,可以在一個調度器上創建若干個協程。調度器負責調度這些協程。並且調度器在其內部維護了一個多路復用器(epoll/select/poll)。

現在假設我們有3個協程A,B,C分別要進行數次IO操作。這3個協程運行在同一個調度器(線程)的上下文中,並依次使用CPU。

協程A首先運行,當它執行到一個IO操作,但該IO操作並沒有立即就緒時,A將該IO事件注冊到調度器中,並主動放棄CPU。這時調度器將B切換到CPU上開始執行,同樣,當它碰到一個IO操作的時候將IO事件注冊到調度器中,並主動放棄CPU。調度器將C切換到cpu上開始執行。當所有協程都被“阻塞”後,調度器檢查注冊的IO事件是否發生或就緒。假設此時協程B注冊的IO事件已經就緒,調度器將恢復B的執行,B將從上次放棄CPU的地方接著向下運行。A和C同理。

這樣,對於某個協程而言,我們采用的是同步的模型;但是對於整個調度器(線程)而言,實際上卻是異步的模型。

好了,原理說完了,我們來看一個實際的例子,echo server。

Copyright © Linux教程網 All Rights Reserved