2. Chrome中的Task
從上面的表不難看出,不論是哪一種消息循環(huán),必須處理的,就是Task(暫且遺忘掉系統(tǒng)消息的處理和Watcher,以后,我們會緬懷它們的...)。刨去其它東西的干擾,只留下Task的話,我們可以這樣認(rèn)為:Chrome中的線程從實現(xiàn)層面來看沒有任何區(qū)別,它的區(qū)別只存在于職責(zé)層面,不同職責(zé)的線程,會處理不同的Task。最后,在鋪天蓋地西紅柿來臨之前,我說一下啥是Task。。。
簡單的看,Task就是一個類,一個包含了void Run()抽象方法的類(參見Task類...)。一個真實的任務(wù),可以派生Task類,并實現(xiàn)其Run方法。每個MessagePump類中,會有一個MessagePump::Delegate的類的對象(MessagePump::Delegate的一個實現(xiàn),請參見MessageLoop類...),在這個對象中,會維護(hù)若干個Task的隊列。當(dāng)你期望,你的一個邏輯在某個線程內(nèi)執(zhí)行的時候,你可以派生一個Task,把你的邏輯封裝在Run方法中,然后實例一個對象,調(diào)用期望線程中的PostTask方法,將該Task對象放入到其Task隊列中去,等待執(zhí)行。我知道很多人已經(jīng)抄起了板磚,因為這種手法實在是太常見了,就不是一個簡單的依賴倒置,在線程池,Undo\Redo等模塊的實現(xiàn)中,用的太多了。。。
但,我想說的是,雖說誰家過年都是吃頓餃子,這餃子好不好吃還是得看手藝,不能一概而論。在Chrome中,線程模型是統(tǒng)一且唯一的,這就相當(dāng)于有了一套標(biāo)準(zhǔn),它需要滿足在各個線程上執(zhí)行的幾十上百種任務(wù)的需求,因此,必須在靈活行和易用性上有良好的表現(xiàn),這就是設(shè)計標(biāo)準(zhǔn)的難度。為了滿足這些需求,Chrome在底層庫上做了足夠的功夫:
- 它提供了一大套的模板封裝(參見task.h),可以將Task擺脫繼承結(jié)構(gòu)、函數(shù)名、函數(shù)參數(shù)等限制(就是基于模板的偽function實現(xiàn),想要更深入了解,建議直接看鼻祖《Modern C++》和它的Loki庫...);
- 同時派生出CancelableTask、ReleaseTask、DeleteTask等子類,提供更為良好的默認(rèn)實現(xiàn);
- 在消息循環(huán)中,按邏輯的不同,將Task又分成即時處理的Task、延時處理的Task、Idle時處理的Task,滿足不同場景的需求;
- Task派生自tracked_objects::Tracked,Tracked是為了實現(xiàn)多線程環(huán)境下的日志記錄、統(tǒng)計等功能,使得Task天生就有良好的可調(diào)試性和可統(tǒng)計性;
這一套七葷八素的都搭建完,這才算是一個完整的Task模型,由此可知,這餃子,做的還是很費功夫的。。。
3. Chrome的多線程模型
工欲善其事,必先利其器。Chrome之所以費了老鼻子勁去磨底層框架這把刀,就是為了面對多線程這坨怪獸的時候殺的更順暢一些。在Chrome的多線程模型下,加鎖這個事情只發(fā)生在將Task放入某線程的任務(wù)隊列中,其他對任何數(shù)據(jù)的操作都不需要加鎖。當(dāng)然,天下沒有免費的午餐,為了合理傳遞Task,你需要了解每一個數(shù)據(jù)對象所管轄的線程,不過這個事情,與紛繁的加鎖相比,真是小兒科了不知道多少倍。。。
圖3 Task的執(zhí)行模型
如果你熟悉設(shè)計模式,你會發(fā)現(xiàn)這是一個Command模式,將創(chuàng)建于執(zhí)行的環(huán)境相分離,在一個線程中創(chuàng)建行為,在另一個線程中執(zhí)行行為。Command模式的優(yōu)點在于,將實現(xiàn)操作與構(gòu)造操作解耦,這就避免了鎖的問題,使得多線程與單線程編程模型統(tǒng)一起來,其次,Command還有一個優(yōu)點,就是有利于命令的組合和擴(kuò)展,在Chrome中,它有效統(tǒng)一了同步和異步處理的邏輯。。。
Command模式
Command模式,是一種看上去很酷的模式,傳統(tǒng)的面向?qū)ο缶幊蹋覀兎庋b的往往都是數(shù)據(jù),在Command模式下,我們希望封裝的是行為。這件事在函數(shù)式編程中很正常,封裝一個函數(shù)作為參數(shù),傳來傳去,稀疏平常的事兒;但在面向?qū)ο蟮木幊讨,我們需要通過繼承、模板、函數(shù)指針等手法,才能將其實現(xiàn)。。。 應(yīng)用Command模式,我們是期望這個行為能到一個不同于它出生的環(huán)境中去執(zhí)行,簡而言之,這是一種想生不想養(yǎng)的行為。我們做Undo/Redo的時候,會把在任一一個環(huán)境中創(chuàng)建的Command,放到一個隊列環(huán)境中去,供統(tǒng)一的調(diào)度;在Chrome中,也是如此,我們在一個線程環(huán)境中創(chuàng)建了Task,卻把它放到別的線程中去執(zhí)行,這種寄居蟹似的生活方式,在很多場合都是有用武之地的。。。
在一般的多線程模型中,我們需要分清楚啥是同步啥是異步,在同步模式下,一切看上去和單線程沒啥區(qū)別,但同時也喪失了多線程的優(yōu)勢(淪落成為多線程串行...)。而如果采用異步的模式,那寫起來就麻煩多了,你需要注冊回調(diào),小心管理對象的生命周期,程序?qū)懗鰜硎青秽粣盒。在Chrome的多線程模型下,同步和異步的編程模型區(qū)別就不復(fù)存在了,如果是這樣一個場景:A線程需要B線程做一些事情,然后回到A線程繼續(xù)做一些事情;在Chrome下你可以這樣來做:生成一個Task,放到B線程的隊列中,在該Task的Run方法最后,會生成另一個Task,這個Task會放回到A的線程隊列,由A來執(zhí)行。如此一來,同步異步,天下一統(tǒng),都是Task傳來傳去,想不會,都難了。。。
圖4 Chrome的一種異步執(zhí)行的解決方案
4. Chrome多線程模型的優(yōu)缺點
一直在說Chrome在規(guī)避鎖的問題,那到底鎖是哪里不好,犯了何等滔天罪責(zé),落得如此人見人嫌恨不得先殺而后快的境地!洞a之美》的第二十四章“美麗的并發(fā)”中,Haskell設(shè)計人之一的Simon Peyton Jones總結(jié)了一下用鎖的困難之處,我罰抄一遍,如下:
- 鎖少加了,導(dǎo)致兩個線程同時修改一個變量;
- 鎖多加了,輕則妨礙并發(fā),重則導(dǎo)致死鎖;
- 鎖加錯了,由于鎖和需要鎖的數(shù)據(jù)之間的聯(lián)系,只存在于程序員的大腦中,這種事情太容易發(fā)生了;
- 加鎖的順序錯了,維護(hù)鎖的順序是一件困難而又容易出錯的問題;
- 錯誤恢復(fù);
- 忘記喚醒和錯誤的重試;
- 而最根本的缺陷,是鎖和條件變量不支持模塊化的編程。比如一個轉(zhuǎn)賬業(yè)務(wù)中,A賬戶扣了100元錢,B賬戶增加了100元,即使這兩個動作單獨用鎖保護(hù)維持其正確性,你也不能將兩個操作簡單的串在一起完成一個轉(zhuǎn)賬操作,你必須讓它們的鎖都暴露出來,重新設(shè)計一番。好好的兩個函數(shù),愣是不能組在一起用,這就是鎖的最大悲哀;
通過這些缺點的描述,也就可以明白Chrome多線程模型的優(yōu)點。它解決了鎖的最根本缺陷,即,支持模塊化的編程,你只需要維護(hù)對象和線程之間的職能關(guān)系即可,這個攤子,比之鎖的那個爛攤子,要簡化了太多。對于程序員來說,負(fù)擔(dān)一瞬間從泰山降成了鴻毛。。。 而Chrome多線程模型的一個主要難點,在于線程與數(shù)據(jù)關(guān)系的設(shè)計上,你需要良好的劃分各個線程的職責(zé),如果有一個線程所管轄的數(shù)據(jù),幾乎占據(jù)了大半部分的Task,那么它就會從多線程淪為單線程,Task隊列的鎖也將成為一個大大的瓶頸。。。
設(shè)計者的職責(zé)
一個底層結(jié)構(gòu)設(shè)計是否成功,這個設(shè)計者是否稱職,我一直覺得是有一個很簡單的衡量標(biāo)準(zhǔn)的。你不需要看這個設(shè)計人用了多少NB的技術(shù),你只需要關(guān)心,他的設(shè)計,是否給其他開發(fā)人員帶來了困難。一個NB的設(shè)計,是將所有困難都集中在底層搞定,把其他開發(fā)人員換成白癡都可以工作的那種;一個SB的設(shè)計,是自己弄了半天,只是為了給其他開發(fā)人員一個長達(dá)250條的注意事項,然后很NB的說,你們按照這個手冊去開發(fā),就不會有問題了。。。
從根本上來說,Chrome的線程模型解決的是并發(fā)中的用戶體驗問題而不是聯(lián)合工作的問題(參見我前面噴的“閑話并發(fā)”),它不是和Map/Reduce那樣將關(guān)注點放在數(shù)據(jù)和執(zhí)行步驟的拆分上,而是放在線程和數(shù)據(jù)的對應(yīng)關(guān)系上,這是和瀏覽器的工作環(huán)境相匹配的。設(shè)計總是和所處的環(huán)境相互依賴的,畢竟,在客戶端,不會和服務(wù)器一樣,存在超規(guī)模的并發(fā)處理任務(wù),而只是需要盡可能的改善用戶體驗,從這個角度來說,Chrome的多線程模型,至少看上去很美。。。
出處:Venus神廟
責(zé)任編輯:bluehearts
上一頁 Chrome的多線程模型 上 下一頁 Chrome的進(jìn)程間通信 上
◎進(jìn)入論壇網(wǎng)絡(luò)編程版塊參加討論
|