——感謝Ian.Sian投遞本文—— 多線程模型是主流的并發(fā)編程模型。在過(guò)去幾十年來(lái),多線程模型一直是開(kāi)發(fā)并發(fā)程序的有力工具。然而,它的歷史并非總那么美好。1997年,NASA 的“火星探路者”號(hào)在執(zhí)行任務(wù)的途中遭遇了嚴(yán)重的時(shí)序異常(參見(jiàn) “What really happend on Mars“,注目 follow-up 中的現(xiàn)身說(shuō)法),無(wú)法發(fā)回探測(cè)數(shù)據(jù)。如果不是 NASA 遠(yuǎn)程刷新了程序,它的結(jié)局就只能是報(bào)廢在火星上。這一切都是由程序中潛藏的一個(gè)優(yōu)先級(jí)反轉(zhuǎn) bug 造成的。更早的例子還有80年代的一系列 Therac-25 型醫(yī)用粒子加速器事故。在這些加速器釋放出的過(guò)量輻射照射之下,數(shù)位病人死亡。事后調(diào)查顯示,至少有一次發(fā)生事故的原因,是加速器的控制軟件中,存在一個(gè)只能由特定操作序列引發(fā)的競(jìng)爭(zhēng)條件 bug。你也許認(rèn)為這些只是陳年往事,但是直到現(xiàn)在,即便是世界500強(qiáng)公司們高價(jià)買(mǎi)來(lái)的信息系統(tǒng),也同樣避免不了這些問(wèn)題。這導(dǎo)致許多程序員認(rèn)為線程是個(gè)潘多拉魔盒,對(duì)它采取能躲就躲的態(tài)度。然而近來(lái)計(jì)算機(jī)的發(fā)展使得躲貓貓的空間越來(lái)越?。弘S便從市場(chǎng)上淘一個(gè)CPU,它里面也有不止一個(gè)核心。未來(lái)的程序員只會(huì)有越來(lái)越多的機(jī)會(huì)接觸到并發(fā)編程,而無(wú)法再獨(dú)善其身了。 加州大學(xué)伯克利分校教授,愛(ài)德華 A. 李在2006年做了一次題為《線程的麻煩 (The Problem with Threads)》的學(xué)術(shù)報(bào)告。在報(bào)告中他提到:看上去,多線程只是對(duì)核心語(yǔ)言的小小擴(kuò)展,甚至可以以第三方庫(kù)的形式存在。但實(shí)質(zhì)上,多線程程序和原有的核心語(yǔ)言編寫(xiě)的程序已經(jīng)完全不同了。其原因在于,由于多線程程序可能以任意的次序交錯(cuò)執(zhí)行,程序再也無(wú)法像順序執(zhí)行時(shí)那樣產(chǎn)生確定的結(jié)果。多線程程序容易編寫(xiě)(因?yàn)閷?xiě)的是順序程序),但是難分析,難調(diào)試,更容易出錯(cuò)。 在我的想法中,產(chǎn)生問(wèn)題的根源,是多線程模型作為對(duì)并發(fā)問(wèn)題的一個(gè)抽象,是很不完善的。抽象的實(shí)質(zhì)是對(duì)問(wèn)題的轉(zhuǎn)換。我們可以把抽象應(yīng)用于一個(gè)問(wèn)題,把它轉(zhuǎn)換成另一個(gè)(或許)更簡(jiǎn)單的問(wèn)題來(lái)解決。解決了轉(zhuǎn)換后的簡(jiǎn)單問(wèn)題,就意味著解決了原有的困難問(wèn)題。嚴(yán)格來(lái)說(shuō),一個(gè)抽象一定要保存原有問(wèn)題的結(jié)構(gòu),同時(shí)去除無(wú)關(guān)細(xì)節(jié)。但是,由于我們生活的世界并沒(méi)有什么東西是完全“嚴(yán)格”的,現(xiàn)實(shí)中使用的抽象有時(shí)會(huì)隱藏解決問(wèn)題的關(guān)鍵細(xì)節(jié),或者殘留一些不該漏出來(lái)的東西。評(píng)價(jià)一個(gè)抽象的好壞,也就不止是看它能節(jié)省多少代碼,和它的界面有多優(yōu)美這么簡(jiǎn)單,同時(shí)還要看看在一個(gè)問(wèn)題被抽象轉(zhuǎn)換之后,留了下來(lái)的細(xì)節(jié)還能不能好好地解決它。 我們可以從這個(gè)意義上理解為什么線程模型是個(gè)很糟糕的抽象。一方面,對(duì)解決問(wèn)題很關(guān)鍵的細(xì)節(jié)(如執(zhí)行次序)被隱藏起來(lái)并受到了粗暴的對(duì)待。另一方面,線程模型極力兼容順序程序的設(shè)計(jì)思想也使得如共享變量這樣的,與線程不兼容的細(xì)節(jié)依然殘留在程序員們的視線之內(nèi)。我們無(wú)力控制程序的執(zhí)行次序,而我們程序的正確性卻依賴(lài)于對(duì)共享變量的有序變更。可以說(shuō),線程提供給我們的抽象簡(jiǎn)直是千瘡百孔。我們還能用它干活,只是因?yàn)槲覀兪掷镞€有加鎖機(jī)制,而它可以部分地堵上線程模型的漏洞。諷刺的是,引入加鎖機(jī)制解決問(wèn)題的同時(shí),又帶來(lái)了新的問(wèn)題,所以我們編寫(xiě)多線程程序總會(huì)遇上死鎖,活鎖,優(yōu)先級(jí)反轉(zhuǎn)……等等。 同樣作為并發(fā)編程問(wèn)題的抽象,角色模型(Actor Model) 比線程模型好就好在,它的資源分享不像線程模型那樣通過(guò)共享變量來(lái)進(jìn)行。角色模型中的資源分享只能通過(guò)特定的機(jī)制(消息傳遞)來(lái)進(jìn)行。你在角色模型里依然可能犯錯(cuò)誤,如你可能制造死鎖,也有可能造成優(yōu)先級(jí)反轉(zhuǎn)。但是沒(méi)有共享變量就意味著沒(méi)有了競(jìng)爭(zhēng)條件,所以絕大部分資源也用不著上鎖了。這樣一來(lái),原先至關(guān)重要的細(xì)節(jié)變得不那么重要,問(wèn)題就這么解決了。 一般來(lái)說(shuō),在修復(fù)一個(gè)糟糕的抽象時(shí),可以采取的策略分如下兩類(lèi):
以 MapReduce 為例,它在解決分布式計(jì)算問(wèn)題時(shí),采取的是第一類(lèi)策略。與現(xiàn)時(shí)流行的做法相反,MapReduce 并不試圖制造計(jì)算是在單一場(chǎng)所完成的假象(流行話講叫“云計(jì)算”),相反它需要程序員自己把問(wèn)題拆分到集群中不同的機(jī)器上。同時(shí),它卻隱藏了大量其他細(xì)節(jié)。這種另類(lèi)策略導(dǎo)致批評(píng) MapReduce “太底層,不通用” 的聲音不絕于耳, 然而這正是 MapReduce 聰明的地方。它放棄面面俱到,集中精力于高效地解決一小類(lèi)問(wèn)題(這類(lèi)問(wèn)題與排序問(wèn)題有類(lèi)似的結(jié)構(gòu)),同時(shí)對(duì)其他的問(wèn)題故意視而不見(jiàn)。它的流行證明了這一策略的成功。 角色模型,通信進(jìn)程(Communicating Sequential Processes, CSP),以及函數(shù)式編程(FP)在應(yīng)對(duì)并發(fā)編程問(wèn)題時(shí)不約而同地選擇了第二類(lèi)策略。它們采用了與并發(fā)兼容性更好的抽象。角色模型與通信進(jìn)程從線程模型的問(wèn)題中抹去了共享變量,純粹 FP 則抹掉了“變量”的可變性。CSP 還可以降低程序執(zhí)行次序的不確定性(因?yàn)樵贑SP中執(zhí)行次序默認(rèn)是確定的,不確定性必須在程序設(shè)計(jì)時(shí)顯式聲明)。由于這些努力,這幾種模型都避免了落入線程模型的麻煩中,得到了對(duì)并發(fā)問(wèn)題的更優(yōu)美的解法。我們可以說(shuō),這些模型提供的抽象比線程模型的都要好。很遺憾的是,它們盡管優(yōu)美,但卻乏人問(wèn)津。角色模型與通信進(jìn)程目前不被任何主流操作系統(tǒng)原生支持(微軟在 Windows 7 附帶的新并行運(yùn)行時(shí) ConcRT 中加入了基于角色模型的 Asynchronous Agents Library,使得狀況稍微改觀了一點(diǎn))。FP 的年歲幾乎和計(jì)算機(jī)語(yǔ)言的歷史一樣古老, 但它的市場(chǎng)份額直到現(xiàn)在也小得可憐。 也許一切都是因?yàn)榫€程模型表面上那迷惑人的簡(jiǎn)單性,以及墨菲定律的變體:布勞爾技術(shù)慣性定律(已經(jīng)成功的技術(shù)在新的,更好的技術(shù)出現(xiàn)時(shí)也會(huì)賴(lài)著不走)。我們?cè)?jīng)接納了一個(gè)有缺點(diǎn)的解決方案,而現(xiàn)在我們被捆綁在這個(gè)方案上了。我們?yōu)榫€程模型寫(xiě)了成百上千萬(wàn)行的代碼,而現(xiàn)在這些代碼的重量束縛住我們的手腳,使得我們無(wú)法前行。 解決線程模型帶來(lái)的問(wèn)題的正確做法,是推廣新的,更完善的模型。既然解決問(wèn)題的阻礙同時(shí)來(lái)自于新技術(shù)的低認(rèn)知度和現(xiàn)有代碼的拖累,很自然地有兩個(gè)方面的工作要做。一、使得新技術(shù)更容易被多數(shù)程序員使用,二、想辦法讓現(xiàn)有的代碼和新技術(shù)兼容。 在兼容老代碼這一頭,我們已經(jīng)有了一些行動(dòng)。微軟在 Windows 7 中提供一個(gè)稱(chēng)為用戶(hù)模式調(diào)度 (UMS) 的功能。UMS 可以將內(nèi)核模式的線程轉(zhuǎn)換為用戶(hù)模式線程,而應(yīng)用程序可以自己提供一個(gè) UMS 調(diào)度器來(lái)調(diào)度它們。這意味著,我們現(xiàn)在有機(jī)會(huì)重載掉系統(tǒng)調(diào)度器的默認(rèn)行為,而根據(jù)應(yīng)用自身的特點(diǎn)給出更合理的調(diào)度安排來(lái)。這個(gè)功能可以用在構(gòu)造更容易使用的并發(fā)模型上,這樣開(kāi)發(fā)的模型可以與老代碼兼容(但 UMS 有一個(gè)讓人迷惑的限制:只能用在64bit 的Windows 7 版本上)。 同樣地,在推廣新技術(shù)方面,現(xiàn)在也有了很多成果。除了角色模型外,事務(wù)性?xún)?nèi)存(這又是一種避免競(jìng)爭(zhēng)條件,從而避免加鎖的方法)正在研究中;CSP 已經(jīng)有了數(shù)個(gè)實(shí)現(xiàn)(如由 Kent 大學(xué)開(kāi)發(fā),針對(duì) Java 的JCSP),同時(shí)還有針對(duì) CSP 的模型檢證工具;至于 FP,最近因?yàn)槿藗冋J(rèn)為 Web 系統(tǒng)的建模可以在函數(shù)式編程范式中更好的表達(dá),F(xiàn)P 正在喚起人們的注意。我們?nèi)钡闹皇O滦录夹g(shù)的成功應(yīng)用范例(實(shí)際上,前面的技術(shù)并不是沒(méi)有成功范例,我們?nèi)钡氖墙?jīng)驗(yàn)?zāi)軌虼笠?guī)模運(yùn)用的范例 ),以及一支理解這些技術(shù)的程序員大軍了。對(duì)于這后一條,我甚至想,既然多線程編程唯一”容易”的事情是寫(xiě)代碼,何不做出一種工具來(lái)讓程序員們可以用寫(xiě)順序程序的思維來(lái)在這些新模型中編寫(xiě)程序呢?這樣的工具會(huì)幫助程序員利用線性程序的思維來(lái)理解代碼,但是同時(shí)又讓人注意到自己的改動(dòng)正在影響系統(tǒng)的哪一部分。如果新模型的代碼變得好理解了,也許更多的人會(huì)使用它們。 (全文完) |
|
來(lái)自: just_person > 《軟件架構(gòu)》