進程、線程和協程,是多任務編程中的常用術語。很多初學者分不清它們之間的區別,今天就以一個小故事爲引子,讓大家搞清楚他們之間的本質區別。
話說在西涼女兒國,大唐文化傳播有限公司CEO唐僧招聘了三個員工做遊戲直播,他們分別是:孫悟空、豬八戒和沙僧。唐僧分別給他們租了三套一室一廳的房子,獨門獨戶,他們三個每人各住一套,獨享各自的衛生間和廚房,互不干擾。這有點類似於進程,在Linux環境下,每個進程有自己各自獨立的 4G 地址空間,大家互不干擾對方,如果兩個進程之間通信的話,還需要藉助第三方進程間通信工具 IPC 才能完成。
如上所示,進程A和進程B,各自有4G地址空間,除了1G的內核空間是共享的,用戶空間則是相互獨立的。不同的進程通過頁表映射,映射到物理內存上各自獨立的存儲空間,在操作系統的調度下,分別輪流佔用CPU去運行,互不干擾、互不影響,甚至相互都不知道對方。在每個進程的眼裏,CPU就是他的整個世界,雖然不停地被睡眠,但是一旦恢復運行,一覺醒來,彷彿什麼都沒發生過一樣,認爲自己擁有整個CPU,一直在佔有它。這就像悟空、八戒和沙僧一樣,各自在自己的房間裏,互相不知道對方。
後來公司不景氣,爲了節省開支,原來的三套房子都退了,唐僧給他們三個租了一套三室一廳的房子,三個人合租。大家可能都跟別人合租過,每個人都有自己的臥室,這是私人空間,其它的像廚房、客廳、衛生間則是公用的。當大家都要使用廚房或衛生間的時候,衝突可能就產生了:比如早上,大家都要使用衛生間,到了晚上大家可能都要使用廚房做飯。到底誰先用?用多久?這是個大難題,比如八戒,喜歡在廚房裏做好吃的,一做就是3小時,紅燒豬蹄、紅燒雞爪......。害得其他人無法做飯,爲此和沙僧衝突不斷;比如悟空,喜歡早上去衛生間蹲坑,一蹲就是倆小時,害得別人早上無法洗涮,上班遲到,爲此跟八戒也是打了好幾架,衝突不斷。
這一點跟線程很類似,在一個進程中,可能存在多個線程,每個線程類似於合租的每個租客,除了自己的私有空間外,還跟其它線程共享進程的很多資源,如地址空間、全局數據、代碼段、打開的文件等等。
當多個線程訪問一些全局變量或共享資源時,可能也會產生競爭和衝突,就像八戒和沙僧搶廚房一樣,八戒的豬蹄在鍋里正燉着呢,沙僧進來二話沒說,就把豬蹄倒在了一邊,搶了鍋開始燒水煮麪條。那該怎麼辦呢?辦法總是有的:唐僧從觀音姐姐那裏借了一把鎖,每天早上和晚上都來到他們三個那裏,誰想用廚房,要先申請鎖,拿到鎖後,你進去,把門鎖上就可以了,別人再急也沒用,等你用好了,解鎖開門,把鎖還給唐僧,其他人拿到鎖,鎖上門,然後才能開始使用廚房。通過這種加鎖解鎖的方式,訪問一些共享資源的衝突和競爭解決了,從此三兄弟和睦相處,再也沒打過架。在線程中,通過各種加鎖解鎖的同步機制,一樣可以用來防止多個線程訪問共享資源產生衝突,比如互斥鎖、條件變量、讀寫鎖等。衝突是解決了,但是開銷卻變大了,上個廁所,還得申請鎖,鎖上門才能用,使用完再開門,釋放鎖,太麻煩了。在線程中也是一樣,不斷地加鎖解鎖,CPU狀態做各種轉換,開銷很大,那怎麼辦?協程此刻就應運而生了。
三兄弟也覺得這樣太麻煩了,唐僧每天過來協調,也很累。後來他們三個經過商量後,決定和諧相處:不再爭搶廚房和衛生間,大家協商着來。悟空蹲坑蹲到一半,便意消了一大半,便主動喊八戒:二師弟,我先出去,你進來吧。八戒很感動,晚上在廚房燉好了豬蹄後,主動喊沙僧:三師弟,我做好了一份菜了,我先吃着,紅燒雞爪我待會再做,你先進來煮麪吧!看看,多麼和諧的三兄弟~
協程和線程一樣,多個協程也共享很多資源:地址空間、代碼段、全局數據等。爲了提高程序運行效率,防止程序阻塞,以及減少不斷加鎖解鎖帶來的開銷,根據需要,協程自己會主動讓出CPU,讓給其它程序執行,而不是像原先的悟空那樣,衛生間上了鎖,在裏面一蹲就是倆小時,佔了茅坑就是不出來,讓其他人在外面乾等。
協程一般常用在網站後臺、手機APP後臺等訪問量巨大的服務器場景中,成千上萬的訪問量,如果使用線程,開銷很大,使用協程可以更好地應對這一場景。但是協程也有缺點,它往往是圍繞一個CPU做文章,無法利用多核。現在很多CPU都是多核多線程了,因此在實際的場景應用中,往往是多進程+協程的形式來彌補這一缺陷,即利用了多核,也減少了多線程帶來的各種開銷,效率大大提高。
以上通過三兄弟合租的故事,讓大家對進程、線程和協程有了一個感性的認識。想要深入理解,還需要進一步的學習和編程實踐。《Linux系統編程》第07期:多線程編程入門視頻教程,和練手項目:使用C語言實現協程編程實戰教程已經更新完畢發佈了,到CSDN學院搜課程名稱或搜索“王利濤”即可。