冒號課堂§2.4:併發範式

第二課 重要範式(4)html

 

2.4併發範式——合做與競爭程序員

在合做中競爭,在競爭中合做                                            ——《競合》算法

 

關鍵詞:編程範式,併發式編程,線程,進程,過程式編程,函數式編程,邏輯式編程,對象式編程數據庫

摘要:併發式編程簡談編程

 

 

預覽瀏覽器

·           有誰願意駕駛一輛啓動後不能剎車、不能倒車、不能變速、油盡方停的汽車呢?安全

·           併發式編程以進程爲導向、以任務爲中心將系統模塊化服務器

·           併發式編程以資源共享與競爭爲主線網絡

 

 

提問數據結構

  • 併發式編程有何特色?
  • 併發式編程有哪些好處?
  • 合理的併發式編程設計應知足哪些條件?

 

講解  

逗號好奇地問:「還有其餘類型的編程範式嗎?」

「不但有,並且有不少。」冒號喝了一口水,悠悠地說,「併發式編程(concurrent programming)就是其中之一。」

歎號有些驚訝:「併發式編程也算一種範式?它彷佛更像是提供運行效率的一種手段。 」

「大謬否則。」冒號搖搖頭,「真正的併發式編程,毫不只是調用線程[1]API或使用synchronized、lock之類的關鍵字那麼簡單。從宏觀的架構設計,到微觀的數據結構、流程控制乃至算法,相比一般的串行式編程均可能發生變化。隨着硬件性能和用戶需求的雙重提高,併發式編程已成爲不可迴避的主題。絕不誇張地說,併發式編程是繼OOP以後又一場思想和技術上的革命。只是相比OOP,儘管年齡相仿,但語言上不夠支持,標準上不夠統一,理論上不夠完善,於是這場革命更具破壞性和建設性。如今咱們來看一個例子,比較兩種燒水泡茶的方案。」

說着冒號在黑板上寫下——

方案一:洗茶杯;放茶葉;灌水壺;燒水;水開後泡茶。

方案二:灌水壺;在燒水的同時,洗茶杯;放茶葉;水開後泡茶。

 

引號見多識廣:「我記得這好像是運籌學中的例子,顯然方案二更佳。從編程的角度來看,方案一是串行式編程,方案二是併發式編程——燒水的線程與洗茶杯放茶葉的線程是同時進行的。」

「若是方案一也用併發式編程呢?」冒號追問。

引號一愣,隨即道:「必須先洗茶杯後放茶葉,洗茶杯放茶葉的同時也無法燒水,至於泡茶,更得等水開以後了。 」

句號明白了冒號的用意:「這就是說,單憑併發式編程並不能保證提升程序性能,還必須在程序設計上下功夫。」

問號繼續深究:「即使如此,若是硬件不支持,併發式編程也未必能提升效率啊。好比在一臺單處理器的主機上,多個線程只能是模擬的、邏輯上的並行,而非真正物理上的並行。」

「這話有必定道理。」冒號有限度地表示贊同,「但設計者應該未雨綢繆,總不能等到有了多處理器纔想到併發式編程吧?再者,沒有多處理器,是否是能夠利用多臺單處理器的主機同時運算呢?退一步講,即便在一臺單處理器的狀況下,併發式編程也是必不可少的。它能保證不一樣用戶、不一樣程序之間的公平競爭,這對多用戶、多任務的系統而言尤其重要。此外,採用併發式編程一樣可能提升性能。最典型的一個例子:當一個線程由於等待某種資源而被堵塞時,能夠切換到其餘線程而不至讓CPU閒置。更重要的一點是,難道除了縮短程序運行時間外,併發式編程就沒有別的好處嗎?譬如打開一個網頁,你是但願瀏覽器邊下載邊顯示網頁呢,仍是下載完成後再顯示?」

 「固然是邊下載邊顯示啦。」 歎號絕不猶豫地說,「若是每一個網頁都是下載完成後再顯示,那還不把人給憋壞了!」

逗號加了一句:「瀏覽器加載文字和加載圖像也應分開在不一樣的線程,若是用戶對文字部分不感興趣,不等圖像顯示就可直接關閉網頁了。 」

「若是網頁被提早關閉,那也是假用戶之手變相縮短了程序運行時間。」冒號接過話來,「還有一個常見的例子,包括瀏覽器在內的許多應用軟件在運行中都會顯示一個進程條。該進程條的更新須要一個單獨的線程,從效率上看它起的做用是負面的,但大大提升了用戶體驗,是軟件人性化的表現。再舉一例,媒體播放器通常都會提供暫停、快進、倒退、快放、慢放等按鈕,若是不採用多線程,它們將形同虛設。此處併發式編程的做用在於提升了軟件的響應能力,也改善了用戶體驗——有誰願意駕駛一輛啓動後不能剎車、不能倒車、不能變速、油盡方停的汽車呢?」

「豈止是不肯意,簡直是恐怖!」歎號加劇了語氣。

「對於操做系統、各類實時系統和諸如數據庫、網絡服務器等基於服務的系統來講,在響應用戶請求的時間上和資源的合理分配上有着極高的要求,併發式編程更是不可或缺。」句號接着補充。

問號隨即又問:「併發式編程還有其餘用處嗎?」

冒號應道:「不一樣編程範式採用不一樣的視角和方法來設計和開發軟件。併發式編程以進程爲導向 (Process-Oriented)、以任務爲中心將系統模塊化。咱們都知道,模塊化在編程中是個好東西。」

引號心存疑惑:「過程式中引入了函數模塊,對象式中引入了對象模塊,但併發式彷佛沒有在語法上引入新型模塊,好比C和Java中的線程其實不過是函數或方法的包裝而已。」

「我猜你的意思是:在定義一個線程以前,其主函數已經模塊化了,不能把功勞記在併發式編程上,對吧?」冒號笑問,「你不能孤立靜止地看待每一個模塊,還要考慮到模塊之間的相互關聯和做用。相比串行式,併發式在模塊之間引入了新的通信和控制方式。也就是說,原先的一些模塊的定義和劃分必定是創建在線程機制的基礎上的。若是失去線程的支持,它們的合理性天然會打上問號,說不定總體設計都會受到牽連。這也體現了編程範式的滲透性和全局影響力。

教室上空瀰漫的疑雲一消而散。

冒號進一步指出:「除了用戶主觀上的需求和硬件客觀上的可能外,併發式顯得格外重要的另外一深層緣由是:許多程序是現實世界的模擬,而咱們生活的世界徹徹底底是併發式的。從某種意義上看,併發式的模擬比對象式的模擬更貼近世界。」

引號再次提問:「併發式編程在設計上有什麼要求?」

「併發式編程以資源共享與競爭爲主線——又是對當今世界形勢的一個逼真模擬。這意味着程序設計將圍繞進程的劃分與調度、進程之間的通信與同步[2]等等來展開。合理的併發式設計須要諸多方面的權衡考量。」冒號說着,放出一段幻燈片——

  • 軟件易於重用、維護、測試
  • 公平有效地利用資源,優化程序性能如增大吞吐量、減小響應時間、提升效率等
  • 保障進程安全,防止競態條件(Race Condition)
  • 保持進程活性,避免死鎖、飢餓、活鎖、資源枯竭等
  • 減小鎖開銷、上下文切換等帶來的性能損失
  • 妥善處理多進程在算法、調試等方面帶來的複雜性

 

歎號蹙眉:「併發式編程好是好,就是太複雜。」

「天下沒有免費的午飯。有所得,必有所失。」 冒號淡淡地說,「併發式編程固然不容易,但也並不是難以掌握。最重要的是,做爲一個程序員,你不得不面對它。即便你不直接用併發式編程,你依賴的代碼和依賴你的代碼也可能用到;即便如今沒有用併發式,未來也可能用到。若是採起避而不理的鴕鳥政策,遲早會被人點中你的死穴。雖然目前不會對併發式編程做更深刻的探討,但但願能引發大家足夠的重視。」

句號談及他的感覺:「相比OOP在語言上獲得的支持,併發式的支持力度好像很不夠。」

冒號點頭稱是:「這是由併發式的複雜性和成熟度決定的。另外,在到底是在語言級別上支持併發、仍是交由操做系統處理的問題上,仁者見仁,智者見智。Ada、Java和C#等選擇前者,在語法上對併發式編程有必定的支持;C和C++等選擇後者,除了關鍵字volatile外,主要靠庫函數支持。專門爲併發式而設計的語言大多僅限於學術研究而非商業應用,Erlang語言[3]是少數的例外。順帶提一下,前面提到的聲明式語言具備無反作用的特性,尤爲適合於併發式編程。」

問號提了一個聽似奇怪的問題:「併發式與前面提到的對象式有無共通之處?」

「併發式與對象式雖是互相正交的兩種範式,倒真有些相通呢。」冒號回答,「它們均與三大核心範式正交,而且愈來愈普遍地向它們滲透着;均爲傳統編程的一種推廣——併發式進程的個數爲一時即爲傳統的串行式編程,對象的方法個數爲爲零即爲傳統的數據類型;均將整個程序系統分解爲若干獨立的子系統,不一樣的是一個以任務爲單位,一個以對象爲單位;子系統之間均能交流與合做,不一樣的是一個以競爭爲主題,一個以服務爲主題。若是將程序系統視做公司,那麼併發式系統是產品型公司,每一個進程是一名工人,其職責是執行單一任務;對象式系統是服務型公司,每一個對象是一名服務員,其職責是提供系列服務。因而可知,一名優秀的程序設計師也應該是一名優秀的管理者。」

句號提出:「迄今爲止咱們談到了五種範式,可否對它們簡單歸納一下?」

「你提問的時機把握得不錯嘛。」冒號開始如數家珍,「爲便於記憶,咱們不妨用‘一二三四五’來歸納編程範式之最:最傳統的一個是命令式;最基本的兩個是命令式和聲明式;最核心的三個是命令式、函數式和邏輯式;最主要的四個是命令式、函數式、邏輯式和對象式;最重要的五個是命令式、函數式、邏輯式、對象式和併發式。最後,咱們來對比一下五大範式。」

少頃,黑板上出現幾行排比句——

過程式:以過程爲模塊的君主體系,模塊之間互相授命與聽命

函數式:以函數爲模塊的數學體系,模塊之間互相替換與合成

邏輯式:以斷言爲模塊的邏輯體系,模塊之間互相概括與演繹

對象式:以對象爲模塊的民主體系,模塊之間互相交流與服務

併發式:以進程爲模塊的生產體系,模塊之間互相競爭與合做

 

「說回併發式編程,它要求咱們擺脫以往習慣的循序漸進的思惟方式,對編程提出了更高的挑戰。程序世界與現實世界同樣,呈百舸爭流、千帆競發之勢,不進則退啊!」言訖,冒號宣佈,「第二堂課到此爲止,歡迎下次光臨。」

 

插語

[1] 併發式編程中以進程(process)或線程(thread)爲基本單位。儘管它們有很大的差別,但爲簡明起見,這裏不加區分地交替使用兩個術語。

[2] 同步(synchronization)只在採用共享內存(shared memory)的併發模型中須要,在採用消息傳遞(message passing)的併發模型中並不須要。本文主要之前一種併發模型爲討論對象,它也是大多數語言或庫(包括C、C++、Java、C#等)所支持的模型。

[3] Erlang是由愛立信開發的一種通用編程語言,支持函數式和併發式,採用消息傳遞的併發模型。

 

總結

·         併發式編程以進程爲導向、以任務爲中心、以資源共享與競爭爲主線。

·         併發式編程有助於提升運行效率、充分利用資源、提升軟件的響應能力、改善用戶體驗、保證公平競爭,同時以進程爲單位將系統模塊化,更加真實地模擬現實世界。

·         合理的併發式設計應該作到:軟件易於重用、維護和測試;有效地利用資源,優化程序性能;保障進程安全和活性;減小性能損失和複雜度。

·         對象式和併發式在傳統編程的基礎上,分別從不一樣的方向進行拓展:對象式在數據類型上進行推廣——容許運算做爲數據類型的成員;併發式在執行順序上進行推廣——容許不一樣運算的執行在時間上交替或者重合。它們與三大核心範式一道,組成了最重要的五大編程範式。

·         五大重要範式對比:

範式

體系

模塊

模塊關係

過程式

君主體系

過程

授命與聽命

函數式

數學體系

函數

替換與合成

邏輯式

邏輯體系

斷言

概括與演繹

對象式

民主體系

對象

交流與服務

併發式

生產體系

進程

競爭與合做

 

「」參考

[1] Abraham Silberschatz,Peter Galvin.Operating System Concepts,5ed..Reading, MA:Addison-Wesley,1998.155-235

 

 

課後思考

·         在你所掌握的語言當中,有哪些是命令式的?哪些是聲明式的?其中,命令式語言裏有哪些聲明式的特徵?聲明式的語言裏有哪些命令式的特徵?它們各自有哪些優勢和缺點?

·         你編寫的程序符合結構化編程的原則嗎?

·         你接觸過函數式語言和邏輯式語言嗎?若是沒有,試着閱讀相關的入門書籍,體會它們與過程式迥然不一樣的風味。(函數式語言推薦Haskell和 Scheme,邏輯式語言推薦Prolog)

·         相比純過程式的編程語言(如C語言),你認爲OOP語言主要有哪些優點?又有哪些劣勢?

·         你認爲在併發式編程設計中,最重要的是什麼?最困難的是什麼?

 

http://www.cnblogs.com/xyz98/archive/2009/03/29/1424666.html

相關文章
相關標籤/搜索