您好,登錄后才能下訂單哦!
這篇文章主要介紹“C語言怎么實現協程”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“C語言怎么實現協程”文章能幫助大家解決問題。
協程是一種用戶空間的非搶占式線程,主要用來解決等待大量的IO操作的問題。
對比使用多線程來解決IO阻塞任務,使用協程的好處是不用加鎖,訪問共享的數據不用進行同步操作。這里需要說明的一點是,使用協程之所以不需要加鎖不是因為所有的協程只在一個線程中運行,而是因為協程的非搶占式的特點。也就是說,使用協程的話,在沒主動交出CPU之前都是不會被突然切換到其它協程上的。而線程是搶占式的,使用多線程你是不能確定你的線程什么時候被操作系統調度,什么時候被切換,因此需要用鎖到實現一種“原子操作”的語義。
其實更一般更常見的做法是,使用非阻塞的IO(比如是異步IO,又或者是在syscall上自己實現的一套異步IO,如asio)并且將處理操作寫在回調函數中。這樣的做法一般沒什么問題,但當回調函數變多,一段連貫的業務代碼就會被拆分到多個回調函數之中,增加維護的成本。因此使用協程可以用同步的寫法寫出效果相當于是異步的代碼。
要實現一個協程,主要的問題是如何保存函數調用的上下文。之前在網上看到一篇博客coroutines in c,用一種非常簡潔的方式實現了這個保存上下文的功能。實現代碼如下:
#define crBegin static int _cr_state = 0; switch(_cr_state) { case 0: #define crReturn(x) do { _cr_state = __LINE__; return x; case __LINE__:; } while (0) #define crFinish } int func1() { crBegin while (1) { printf("hello world\n"); crReturn(0); } crFinish }
這個代碼利用了函數的static變量來保存函數調用狀態。注意,由于vs2013有一個調試特性,所以vs2013的__LINE__的實現不是常量因此會編譯不通過,使用gcc就可以編譯。這段代碼簡單是簡單但是有問題,比如說如果兩個協程調用同一個函數,就會出錯。因此博客里面提及這段代碼主要是給出一個思路,如果實際使用的話這樣子肯定是不行的。
前面說過,實現協程最主要的是保存函數的調用的上下文,而這些上下文主要就兩個部分:1.各個寄存器的值,2.函數調用棧。C語言里可以通過setjmp來保存函數調用時,各寄存器的值。保存之后,便可以通過longjmp重現回到當初setjmp的地方(可以理解成跨函數的goto)。但是,需要注意的是,setjmp僅負責保存寄存器的值,不負責維護其函數調用棧(這個看看setjmp的jmp_buf的結構就知道了),因此必須由使用者來手動的維護這個函數調用棧。使用setjmp、longjmp的一個常見的錯誤就是,嘗試去longjmp到一個已經執行完的函數,這時候雖然寄存器的值是當時保存的值,但是調用棧已經不是原來的調用棧了。
而我的做法是,在創建一個協程的時候在堆上申請一塊空間(大小為2M)作為協程的調用棧,然后在setjmp的時候,手動更改寄存器esp的值,使其指向這個我自己創建的調用棧。因此在以后運行的時候,這個協程就會使用我提供的那塊內存作為棧。
我的這個協程庫提供了三個接口:
coro_new:創建一個協程
coro_yield:將控制權返回給調度協程
coro_main:運行調度協程
協程的控制流程如下:
通過coro_main運行調度協程,并找出下一個運行的協程,運行之。
運行這個協程直到其調用coro_yield將控制權返還給調度協程。
重復以上兩個步驟,直到所有協程運行完畢。
這個協程庫實現的非常簡單,只有100來行的代碼,當然實現它的目的是為了提供一個最簡單的協程模型,而不是一個功能完整、魯棒性強的能投入實際業務運行的協程。
因此問題還是有很多的:
比如當在協程里面調用棧超過2M時,這個是需要處理的,現在的代碼是沒有做的,理應中斷程序,避免寫壞堆,產生隨機的不可重現的問題。
顯然在實現時沒有考慮到多線程,如果在多線程環境里面運行,需要代碼做同步處理。
現在的這個版本的協程有一個約定,在協程里調用的函數不能阻塞在syscall,這顯然也是不科學的。一個完整的協程庫,應該包含一些常用的syscall的非阻塞的實現,畢竟只有一個線程不能真的阻塞在這個調用上。
關于“C語言怎么實現協程”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。