理解協程,以及協程如何提升IO阻塞時的性能

閱讀這篇文章,你將會了解:
1.協程的概念
2.進程、線程、線程的區別
3.協程有什麼好處,如何提升性能

一.概念:

  1. 協程是在線程下的,線程內的多個協程通過協程的調度獲得cpu時間。
  2. 線程內的多個協程是串行的。即線程下的某個協程在運行時,其他協程必然是掛起,沒有在運行。
  3. 一個進程可以有多個線程,一個線程可以用多個協程。

二.進程、線程、協程三者的上下文切換比較

進程 線程 協程
切換者 操作系統 操作系統 用戶(編程者/應用程序)
切換時機 根據操作系統自己的切換策略,用戶不感知 根據操作系統自己的切換策略,用戶不感知 用戶自己的程序決定
切換內容 頁全局目錄,內核棧,硬件上下文 內核棧,硬件上下文 硬件上下文
切換內容的保存 保存於內核棧中 保存於內核棧中 保存在用戶自己的變量(用戶棧或堆)
切換過程 用戶態 - 內核態 - 用戶態 用戶態 - 內核態 - 用戶態 用戶態(沒用進入內核態)
切換效率

三.協程帶來了什麼好處

從編程風格的角度看:

很多人在理解協程的時候,都會覺得協程就像個語法糖。很多的時候就像是一個異步調用的實現,等一個“I/O阻塞”的方法完成後,再切回來恢復上下文繼續完成接下來的邏輯。所以總結來說,這種風格的編程更易於理解,而且一樣方便於代碼的複用。

從性能的角度看:

1.在學習的過程中,讓我最疑惑的一點是協程能否解決因“I/O阻塞”引起的性能問題?

總所周知,在我們實際的程序中遇到一些“I/O阻塞”的操作時,我們總是希望對應的線程能自覺的讓出CPU時間片給其他線程,直到I/O完成後通過一些機制喚醒之前掛起的線程,這樣能夠提升CPU的利用率,提升程序的性能。
一個線程可以擁有多個協程,當協程運行過程中遇到了“I/O阻塞”的操作,如果協程無法讓出時間片,將會導致其他的協程仍然無法運行,那麼協程實際並沒有解決“I/O阻塞”引起的性能問題。
所以,假若協程想解決這個問題,那麼協程實現庫必須是實現了一些底層的監聽,例如發現當前協程即將要進行“網絡I/O”時(假設當前操作系統的網絡IO模型是epoll),協程庫能劫持該系統調用,把這些IO操作註冊到系統網絡IO的epoll事件中(包括IO完成後的回調函數),註冊完成則把當前協程掛起,讓出cpu資源給其他協程。直到該IO操作完成,通過異步回調的方式通知該協程庫去恢復之前掛起的協程,協程繼續執行。
在實際場景中,不只是網絡IO,硬盤的讀寫也會引起IO阻塞,包括我們常用的數據庫讀寫、文件讀寫等。所以協程能否在這方面提升性能,要看對應協程庫能否感知該操作,能否支持異步。

2.協程的同步寫法卻能擁有異步的性能?

這個問題實際上在上面已經得到解答了。雖然協程的使用是通過同步的寫法實現的。如果希望得到性能上的提升,實際上還是通過異步回調的支持,只不過這個異步回調是底層模型上完成的。所以上協程上關於同步異步的性能是建立在底層的異步模型上的。

3.協程佔用的空間可以比線程小

協程是用戶定義的,所以在設計協程時,協程的最小空間佔用可以比線程小很多。在一個服務器很難創建10W個線程,但是可以輕鬆的創建10W個協程。

4.協程切換比線程的切換更輕量

先控制一下變量,不考慮阻塞的問題
假設4核的cpu,100個任務併發執行
(1)創建100個線程:100個線程參與cpu調度。
(2)創建10個線程,每個線程創建10個協程:10個線程參與cpu調度,每個協程內的10個協程由協程庫自己進行調度。

理論上,進程擁有100個線程時,每個線程的時間片會比擁有10個線程時短,也就意味着在相同時間裏,擁有100個線程時的上下文切換次數比擁有10個線程時多。

所以協程併發模型與多線程同步模型相比,在一定條件下會減少線程切換次數,但是增加了協程切換次數,由於協程的切換是由協程庫調度的,所以很難說協程切換的代價比省去的線程切換代價小,合理的方式應該是通過測試工具在具體的業務場景得出一個最好的平衡點。

四.參考文檔:

https://www.cnblogs.com/laiy/p/coroutine.html
https://blog.csdn.net/chengqiuming/article/details/80573288

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