Linux下c實現協程-Coroutine__Linux

來源:互聯網
上載者:User
Widnows 是提供了使用者級線程的,類似 coroutine 需要使用者主動是切換。這在單線程程式中非常有用。線程調度模組只負責提供堆棧,環境的儲存。不負責分配時間片等。


自己實現 coroutine 並不難,但能用作業系統提供的可以得到更多的便利。Windows 中把這種使用者級線程叫做 Fiber,纖維的意思。比較通用的譯名是纖程。


我們可以把一個 thread 轉換成一個 fiber ,用到的 API 是 ConvertThreadToFiber。其實用的更多的是CreateFiber,它可以建立一個纖程,但並不切換過去運行。


被建立出來的 Fiber 會有一個內容相關的地址被返回,用於以後的切換操作。我們可以用 SwitchToFiber 來切換。這是唯一用於 Fiber 釋放操作權的途徑。SwitchToFiber 必須顯式的指定切換的目標,所以 Fiber 調度的工作需要我們自己寫代碼來實現。


GetCurrentFiber 和 GetFiberData 這兩個函數都很有用,一個用來取到運行環境,一個用來取得建立參數,這兩個函數都是用 inline 函數的形式提供在 .h 檔案中的。


Fiber,也就是纖程,完全運行在使用者態,各個線程的切換也只在使用者態完成避免了系統調用,所以切換開銷較小。線程的調度,通常是由作業系統的線程調度器完成,在現代OS中,通常使用搶佔式調度策略。而纖程的調用,完全依賴於程式員自己,即實現一種合作式調度,只有在主動提出切換時,才會進行切換。


多線程的程式很多不是為了提高效率,充分利用多 CPU,而是為了邏輯描述方便。


纖程往往可以提供更加便捷的描述,因為它只能通過 SwitchToFiber 顯式切換,這樣可以少許多加解鎖的過程。而且切換的效率也比真正的線程來的高。


Fiber 就是一種 coroutine 的實現,而 coroutine 其實是一種常見的演算法表達方式。只是 C 語言和其衍生的基於一個環境堆棧的語言並沒有直接支援。所以才少為人知。


纖程用於化非同步為同步。
你可以進行一個非同步作業以後就切換纖程,等到非同步作業完成以後在切換回來,這樣,在邏輯上相關的代碼就可以寫到一個函數裡面,而不用人為的分到多個回呼函數中
fiber 的好處應該可以減輕鎖的顧慮。在沒有主動切換上下文之間,可以看成是原子操作。


要自己實現fiber,主要是儲存線程上下文。注意要儲存的資訊有:各個寄存器,棧頂和棧底,異常資訊,浮點寄存器。 window Fiber的預設堆棧是1M




How to implement fiber: http://blog.csdn.net/seizef/article/details/6567301






Linux
Linux中有context api在ucontext.h中,可以認為是一個先進的版本 setjmp/longjmp, 可以用來完成類似的功能。我們調用getcontext/makecontext,來初始化纖程,用swapcontext/setcontext來切換纖程。
makecontext 建立一個新的上下文,可以傳入我們自己定義的棧空間。它記錄CPU的各種資訊
swapcontext 切換上下文(會儲存當前上下文並切換到另一個上下文),其實就是改變CPU的相關寄存器 狀態,替換到另外的可執行檔上下文中
getcontext用於儲存當前上下文,
setcontext用於切換上下文


實現使用者線程的過程是:我們首先調用getcontext獲得當前上下文,然後修改ucontext_t指定新的上下文。同樣的,我們需要開闢棧空間,但是這次實現的線程庫要涉及棧生長的方向。然後我們調用makecontext切換上下文,並指定使用者線程中要執行的函數。
還有一個挑戰,即一個線程必須可以主動讓出CPU給其它線程。swapcontext函數可以完成這個任務


setjmp/longjmp
說到這裡,我們不得不提起另一個看似可以用來完成類似工作的古老的C API,setjmp/longjmp。setjmp用來儲存當前的執行環境,longjmp用來還原上次的執行,這樣可以實現non-local goto的功能。但是這裡有一個問題,就是當我們調用longjmp回到setjmp儲存的狀態繼續執行時,如果longjmp的調用者與setjmp的調用者不是同一個函數,那麼longjmp所在的棧的狀態將是undefined[1]。也就是說,我們不能通過在longjmp之前,save狀態,之後再longjmp回來,這是未定義的行為。當然,在win32上,你可以這麼做,而且還工作地很好(我在實現中使用了這一技巧)。但是在linux上,你將會遇到執行階段錯誤,提示stack smashing(gcc的stack保護機制,__fortify_fail),也就是棧被破壞了(這令我debug了很長時間,最後放棄,網上看到許多實現用這方法,但是不奏效)。setjmp/longjmp還有其他陷阱,比如,在win32上,他不會儲存/恢複SEH異常鏈,等等。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.