協(xié)程是一種比線程更加輕量級的存在,協(xié)程完全由程序所控制(在用戶態(tài)執(zhí)行),一個線程可以有多個協(xié)程。
多線程工作時,若用線程實現(xiàn)兩大弊端,一是系統(tǒng)線程會占用非常多的內存空間;二是過多的線程切換回占用大量的系統(tǒng)時間。
協(xié)程可以解決以上2個問題,協(xié)程運行在線程之上,當一個協(xié)程執(zhí)行完成后,可以選擇主動讓出,讓另一個協(xié)程運行在當前線程之上。協(xié)程并沒有增加線程數(shù)量,只是在線程的基礎之上通過分時復用的方式運行多個協(xié)程,而且協(xié)程的切換在用戶態(tài)完成,切換的代價比線程從用戶態(tài)到內核態(tài)的代價小很多。

協(xié)程的注意事項
實際上協(xié)程并不是什么銀彈,協(xié)程只有在等待IO的過程中才能重復利用線程,上面我們已經(jīng)講過了,線程在等待IO的過程中會陷入阻塞狀態(tài),意識到問題沒有?
假設協(xié)程運行在線程之上,并且協(xié)程調用了一個阻塞IO操作,這時候會發(fā)生什么?實際上操作系統(tǒng)并不知道協(xié)程的存在,它只知道線程,因此在協(xié)程調用阻塞IO操作的時候,操作系統(tǒng)會讓線程進入阻塞狀態(tài),當前的協(xié)程和其它綁定在該線程之上的協(xié)程都會陷入阻塞而得不到調度,這往往是不能接受的。
因此在協(xié)程中不能調用導致線程阻塞的操作。也就是說,協(xié)程只有和異步IO結合起來,才能發(fā)揮最大的威力。
那么如何處理在協(xié)程中調用阻塞IO的操作呢?一般有2種處理方式:
在調用阻塞IO操作的時候,重新啟動一個線程去執(zhí)行這個操作,等執(zhí)行完成后,協(xié)程再去讀取結果。這其實和多線程沒有太大區(qū)別。
對系統(tǒng)的IO進行封裝,改成異步調用的方式,這需要大量的工作,最好寄希望于編程語言原生支持。
協(xié)程對計算密集型的任務也沒有太大的好處,計算密集型的任務本身不需要大量的線程切換,因此協(xié)程的作用也十分有限,反而還增加了協(xié)程切換的開銷。
總結
在有大量IO操作業(yè)務的情況下,我們采用協(xié)程替換線程,可以到達很好的效果,一是降低了系統(tǒng)內存,二是減少了系統(tǒng)切換開銷,因此系統(tǒng)的性能也會提升。
在協(xié)程中盡量不要調用阻塞IO的方法,比如打印,讀取文件,Socket接口等,除非改為異步調用的方式,并且協(xié)程只有在IO密集型的任務中才會發(fā)揮作用。
協(xié)程只有和異步IO結合起來才能發(fā)揮出最大的威力。