一個C/C++協程庫的思考與實現之協程的簡單調度

https://github.com/DoasIsay/ToyCoroutine

 

提供這樣的協程使用接口,實在令人,,,

 

0.0.1代碼的accept協程只能寫的這樣醜陋,因爲協程創建後立馬調度執行佔用了CPU,只有當協程讀寫網絡IO將被阻塞時纔會主動讓出CPU,這時調度器會獲得CPU,當有新連接到來時accept協程纔會被調度器調度恢復執行,所以要在新創建一個協程前要先保存accept協程的上下文環境做爲一個返回點,以便將來返回

 

對於這麼醜陋的代碼我耿耿於懷,事實是我寫代碼的水平也確實不怎麼樣,確實醜陋

但我還是想寫出像創建多線程那樣的代碼,比如這樣

要寫出這樣的代碼,就需要把所有協程上下文的保存與恢復都在協程庫中實現,不能讓它出現在用戶代碼中,該如何做?

 

在調度器中加入一個先進先出的runQueue隊列,協程創建後,先把協程push到隊列,然後提供一個schedule接口,調用schedule接口後,控制權轉移到調度器,調度器從runQueue,pop一協程運行,簡直完美,流程如下:

 

  1. accept協程創建
  2. push此協程到runQueue,調用schedule接口,讓出CPU控制權轉移到調度器
  3. 調度器epoll_wait超時,從runQueue,pop出一協程調度運行
  4. accept協程運行,無連接可接收,讓出CPU控制權轉移到調度器,註冊epoll讀事件 ,然後epoll_wait等待事件或超時返回
  5. 客戶端新建連接,epoll_wait返回讀事件,調度器調度accept協程恢復執行
  6. accept協程接收連接,創建socketHandle協程,push協程到runQueue
  7. accept協程繼續運行,同4
  8. epoll_wait超時,同3
  9. socketHandle協程被調度運行,開始read數據,無數據可read,讓出CPU,控制權轉移到調度器,註冊epoll讀事件,然後epoll_wait等待事件或超時返回

 

看,我所謂的協程調度就是如此的簡單,first come first service

 

 

 

有了這個runQueue後我發現,好像可以創建調度無網絡IO的協程了,0.0.1的代碼只能創建調度網絡IO協程,因爲在讀寫不能滿足,任務將被OS阻塞時是一個調度點,採用非阻塞的IO,當系統調用返回EAGAIN 或EWOULDBLOCK時表示當前操作無法滿足,此時我們可以選擇讓協程主動讓出CPU,把控制權轉移到調度器,但無網絡IO協程是沒有IO讀寫的,因此就無法實現它的調度,所以只有runQueue顯然還不能實現無網絡IO協程的調度,還要提供一個類似schedule的接口,讓當前協程主動讓出CPU,那就是yield了

 

於是在0.0.2的代碼中可以寫出這樣的代碼了,這實在是太酷了唉

 

其實我並沒找到yield接口有什麼使用場景,實現yield接口只是因爲覺得這種代碼很酷而已,它還有一個使用場景就是可以寫出如下代碼做爲一種週期性執行的任務,每次執行完任務後就yield讓出CPU等待下一次調度,再加個timeout參數就是一個sleep了

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章