概念: 並行
並行性是指事物在系統中同時發生的傾向。在處理軟體系統的並行性問題時,通常有兩方面比較重要:能夠偵測和回應隨機發生的外部事件、確保在最短的時間內回應這些事件。
關係
主要說明
附註:本文僅針對「並行性」進行廣泛性的討論,因為並行性可能出現在任何系統中。不過,對於必須即時回應外部事件且通常必須在一定期限內解決的系統,並行性格外重要。為了處理這類系統的特殊需求,「Rational Unified Process (RUP)」提供「即時(反應)系統延伸模組」。如需本主題的相關資訊,請參閱即時系統

什麼是並行性?

並行性是指事物在系統中同時發生的傾向。毫無疑問,並行性是一種自然現象。在真實世界裡,隨時會有許多事物同時發生。我們在設計軟體來監視和控制實際系統的同時,也必須處理這種自然發生的並行性。

在處理軟體系統的並行性問題時,通常有兩方面比較重要:能夠偵測和回應隨機發生的外部事件、確保在最短的時間內回應這些事件。

如果每一個並行活動是單獨形成且互不交集,則解決方式相當簡單:我們只需建立個別的程式來處理每一個活動即可。設計並行系統的困難之處,通常在於並行活動之間的互動。並行活動間產生互動時,則必須進行某種程度的協調。

圖解說明詳見下文。

圖 1: 實際並行範例:不互動的並列活動比較沒有並行問題。當並列活動之間產生互動或共用相同的資源時,此時就值得注意並行問題。

交通流量就是一種很好的比喻。如果並行車流能分布行駛在數條不同的獨立車道上,自然就比較不會有交通問題。但在相鄰車道上行駛的並行車流則需要一些協調,以確保安全地變換車道。而十字路口上則會出現更困難的會車情形,需要更周詳的協調。

為何要注意「並行性」?

並行性的部份推動因素來自於外部。也就是由環境的需求所引起。在實際系統中,許多事物會同步發生,必須由軟體「即時」解決。因此,許多即時軟體系統必須具有「反應」能力。必須回應外部不定時、不按順序發生的事件。

設計傳統的流程化程式來處理這些狀況會相當複雜。不如將系統分割成並行的軟體元素來處理每一個事件還「可能」來的簡單的多。這裡用「可能」的語氣,因為複雜性也會受到事件之間互動程度的影響。

並行性的原因也可能源自於內部 [LEA97]。如果有多顆 CPU,則同時執行作業可以明顯加速系統的計算工作。即使在單一處理器之內,多工機制可避免一項活動在等待 I/O 時阻擋另一項活動,一樣可以顯著加速處理事物。這種情形常見於系統開機時。通常會有許多元件,各元件在運作之前皆需要一段準備時間。如果依序執行這些作業,緩慢的速度將令人無法接受。

並行性也可以加強系統的操控性。例如,一個功能可以在中途啟動、停止或影響另一個功能 - 在無並行元件的情況下,這將難以達成。

並行軟體為何很難?

有這麼多的好處,我們為何不全部採用並行程式設計呢?

大部份的電腦和程式語言天生就是序列化。一個流程或處理器一次只執行一個指令。在單一序列化處理器之內,必須透過交錯執行不同的作業來產生並行性的假象。困難之處並不在於作法,關鍵在於何時和如何精準地交錯執行彼此可能互動的程式區段。

雖然多個處理器可以輕易達成並行性,然而互動情形會更加複雜。首先,在不同處理器執行的作業之間,有通訊方面的問題。通常會涉及多層軟體,導致複雜性和時序負荷更高。決定論在多 CPU 系統中並不成立,因為時脈和時序不同,且元件可能各自失敗。

最後,由於缺少明確的整體系統狀態,也是造成並行系統難以掌握的原因之一。並行系統的狀態是所有元件的狀態總合。

並行、即時系統範例:電梯系統

我們以電梯系統為例來說明所討論的概念。更精確的說法是一部設計用來控制建築物中某位置上之一組電梯的電腦。很明顯,一組電梯可能同時有許多事件發生,也可能什麼都沒發生!任何樓層隨時可能有人要求搭電梯,也有人在等待搭電梯。有些電梯可能閒置,有些可能正在載運乘客、前往搭載樓層,或兩者同時發生。電梯門必須適時開啟和關閉。乘客可能堵住電梯門、按下開門或關門按鈕,或選擇樓層之後又改變心意。顯示板需要更新、馬達需要控制,諸如此類,這一切都由電梯控制系統密切監控。基本上,這是一個不錯的模型來探索並行概念,且大家對它的運作方式及專用詞彙都有一定程度的瞭解。


圖解說明詳見下文。
圖 2:  情境中有兩部電梯和五位分散於 11 個樓層的潛在乘客。

當潛在乘客在不同時間提出需求時,系統會根據現行狀態和預計的回應時間,選擇電梯來反應,以提供最佳的整體服務。例如,當第一位潛在乘客 Andy 要求搭電梯下樓時,由於兩部電梯剛好閒置,因此由最近的電梯 Elevator 2 回應(必須向上來接送 Andy)。但是,稍後當第二位潛在乘客 Bob 要求搭電梯上樓時,則由較遠的 Elevator 1 回應,因為已知 Elevator 2 必須先下降到未知的目標樓層後,才能自底下樓層回應提出的上樓呼叫。

簡化的並行性策略

如果電梯系統只有一部電梯,且電梯一次只需要搭載一位乘客,我們可以大膽假設以平常的序列化程式就足以應付。即使是如此「簡單」的例子,程式也需要許多分支來因應不同的狀況。比方說,如果乘客一直沒有出電梯門,也未選擇樓層,我們就要重設電梯來回應其他呼叫。

這種為了處理多位潛在乘客的呼叫和多位乘客的要求所衍生出的一般需求,就是我們先前討論的並行性外部動因的最佳例子。因為潛在乘客是各自決定同時發生的行為,不論電梯的狀況如何,幾乎隨時可能要求搭乘電梯。若企圖設計一個可依先前決策來持續操縱電梯,但又能回應這些隨時發生的外部事件的序列化程式,這將會相當困難。

並行性抽象化

為了有效率地設計並行系統,我們必須推論並行性在系統中的角色,因此,需要並行性本身的抽象概念。

並行系統的基本建置區塊是彼此互動或獨立進行的「活動」。Buhr 的「時間緒」是一個適合分析這些活動的抽象圖形。[BUH96] 實際上,圖 3 的電梯情境就是採用其中一種格式。每一個活動以一條線表示,活動即沿著這條線進行。大圓點代表活動開始的位置,或等待發生事件之後再繼續的位置。一個活動可以促使另一個活動繼續執行,在時間緒表示法中,這以觸碰另一個時間緒上的等候位置來表示。

圖解說明詳見下文。

圖 3:  執行緒的想像圖

軟體的基本建置區塊是程序和資料結構,但只有這些尚不足以分析並行性。處理器在執行程序時會根據現行條件,沿著特定的路徑來執行。這個路徑就稱為「執行緒」或「控制緒」。這個控制緒可能會視當時情況選擇沿著不同的分支或迴圈進行;在即時系統中,可能會暫停一段指定的時間,或等到排定的時間再繼續執行。

從程式設計師的觀點來看,執行緒由程式的邏輯來控制,而由作業系統來排定執行。當軟體設計師選擇以一個程序呼叫其他程序時,執行緒會從一個程序跳至另一個程序,等到碰到 return 陳述式時,再跳回原本離開的位置繼續執行。

從 CPU 的觀點來看,只有一個主要執行緒穿梭在軟體中,輔以一些回應硬體中斷的突發執行緒。由於一切都是以此模型為基礎,設計師有必要瞭解此模型。即時系統的設計師比其他軟體類型的設計師,必須更仔細瞭解系統的運作方式。不過,這個模型抽象程度太低,只能非常粗略地表示 CPU 的並行性。若要設計複雜的系統,有必要進行各種程度的抽象化。當然,抽象是指建立一種觀點或模型,藉由除去不必要的細節,讓我們將焦點集中在當前問題的重要事物上。

再往上一層,我們通常以「層次」的字眼來表示軟體。在最底層,作業系統 (OS) 夾在硬體和應用軟體之間。OS 對應用程式提供硬體服務,例如記憶體、計時及 I/O,但將 CPU 抽象化,建立一個與實際硬體配置無關的虛擬機器。

實現並行性:機制

管理控制緒

為了支援並行性,系統必須提供多個控制緒。軟硬體可以透過許多方式來實作抽象的控制緒。大多數常見的機制都是從下列幾個基本原理衍生而來 [DEI84],[TAN86]:

  • 多處理 - 多個 CPU 並行執行
  • 多工 - 作業系統藉由交錯執行不同作業的方式,
    在單一 CPU 上模擬並行性
  • 應用程式解決方案 - 應用軟體負責適時地換到
    不同的程式碼分支

多工

當作業系統提供多工機制時,常見的並行單位是流程。流程是作業系統提供、支援和管理的實體,唯一的用途就是提供程式的執行環境。流程提供記憶體空間給應用程式專用、提供執行緒以執行流程,可能還提供一些與其他流程之間收送訊息的方法。事實上,流程是虛擬的 CPU,可以執行應用程式的並行組件。

一個流程有三種可能的狀態:

  • 暫停 - 等待接收輸入或取得某些資源的控制權;
  • 備妥 - 等待作業系統給予執行的機會;
  • 執行中 - 實際使用 CPU。

通常也會指定相對優先順序給流程。作業系統核心會隨時根據流程的狀態、優先順序及一些排程法原則,決定執行哪一個流程。多工作業系統在所有流程之間實際上是共用單一控制緒。

附註:雖然「作業」和「流程」這兩個術語通常可以互通。但「多工」通常代表一次管理多個流程的能力,而「多處理」是指具有多顆處理器 (CPU) 的系統。因為沿用成習,我們就遵守這個慣例。不過,我們較少採用「作業」這個術語,只有在為了明確區別完成的工作單位(作業)與提供資源和環境的實體(流程)時才會採用。

如先前所述,從 CPU 的觀點來看,只有一個執行緒。就像應用程式可以經由呼叫子常式在程序之間跳躍一樣,作業系統在遇到中斷、程序完成或其他事件時,也會將一個流程的控制權移轉給另一個流程。由於流程會提供記憶體保護,因此這種「作業切換」會伴隨產生相當大的額外負荷。再者,就應用程式的見解而言,因為排程法原則和流程狀態並不重要,流程交錯通常只是很低度的抽象化,不足以分析應用程式的重要並行性類型。

為了清楚分析並行性,必須明確區分執行緒概念和作業切換概念的差異。每一個流程會維護自己的執行緒。當作業系統在流程之間切換時,一個執行緒會暫時中斷,另一個執行緒會啟動或從先前離開的位置繼續執行。

多重執行緒作業

許多作業系統會提供「輕型」的替代流程,稱為「執行緒」或「輕型執行緒」,尤其是在即時應用程式的作業系統中。

執行緒是一個流程內達成更細微的並行性的一種手段。每一個執行緒屬於一個流程,一個流程中的所有的執行緒共用單一記憶體空間及此流程控制的其他資源。

通常一個執行緒會指定一個執行流程。

附註:很遺憾地,「執行緒」這個字有多種意義。在本文中,單獨出現「執行緒」時,代表作業系統提供和管理的「實體緒」。我們在先前的討論中提及「執行緒」、「控制緒」或「時間緒」時,代表的是一種抽象概念,不見得與實體緒有關。

多處理

毫無疑問,多顆處理器提供實現並行執行的機會。在大多數情況下,每一個作業會永久地指定至特定處理器的一個流程,但在某些情況下,作業也可能動態地指定至下一個可用的處理器。最簡單的作法可能是利用「對稱多重處理器」。在這種硬體配置中,多顆 CPU 可以透過一個共用匯流排來存取記憶體。

支援對稱多重處理器的作業系統,可以動態將執行緒指定至任何可用的 CPU。例如 SUN 的 Solaris 和 Microsoft 的 Windows NT,即為支援對稱多重處理器的作業系統。

並行軟體的基本問題

稍早,我們表示並行性會增加和降低軟體的複雜性,這種說法似乎自相矛盾。並行軟體以簡單的方法解決複雜的問題,主要是因為允許並行活動「各司其職」。就此而論,並行性只不過是加強軟體模組化的工具。當系統必須執行非常獨立的活動,或回應非常獨立的事件時,只要指定至個別的並行元件,自然就可以簡化設計。

造成並行軟體漸趨複雜的原因,絕大多數是因為這些並行活動幾近(但不完全)獨立。換句話說,複雜性起因於互動。從情境的觀點來看,非同步活動之間的互動一定牽涉到交換某些信號或資訊。並行控制緒之間的互動會產生一連串並行系統的特定問題,而這些問題必須加以解決以保證系統能夠正常運作。

非同步和同步互動的比較

雖然跨流程通訊 (IPC) 或跨執行緒通訊機制有許多不同的實際作法,但追根究底可以歸為兩類:

非同步通訊中,傳送活動在轉遞資訊時,不考慮接收端是否可以接收。送出資訊之後,傳送端就接著繼續進行下一個任務。如果接收端還無法接收資訊,資訊會放入佇列中,稍後再由接收端擷取。傳送端和接收端彼此之間非同步地運作,也就無法假設對方的狀態。通常,非同步通訊又稱為 訊息傳遞

同步通訊除了交換資訊以外,還包括傳送端和接收端之間的同步化。事實上,在資訊交換期間,兩個並行活動會合併在一起,執行一段共用的程式碼,等到通訊完成時再分開。因此,在這段期間,兩個活動彼此同步,不會發生並行性衝突。如果有一個活動(傳送端或接收端)比另一個活動更早準備開始通訊,則將會暫停,等待另一個活動也準備好開始通訊。因此,這種通訊模式有時又稱為 會合

同步通訊有個潛在的問題,在等待對方備妥之前,活動無法回應其他任何事件。許多即時系統中不一定能接受這種限制,因為無法保證及時回應重要的狀況。還有,容易發生死結是另一項缺點。兩個以上的活動陷入彼此等待對方的無限迴圈時,即為死結。

當並行活動之間需要互動時,設計師必須選擇要採用同步或非同步樣式。如果採用同步,則表示兩個以上的並行控制緒必須在單一時間點上會合。這通常意謂著一個控制緒必須等待另一個控制緒回應要求。 例如,並行活動 A 需要取得並行活動 B 的資訊,才能繼續自己的工作,就是一種最簡單和最常見的同步互動情形。

毫無疑問,非並行軟體元件以同步互動為標準。一般程序呼叫是同步互動的最佳例子:當一個程序呼叫另一個程序時,呼叫端會立即將控制權移轉給被呼叫的程序,然後就開始「等待」直到重新取回控制權為止。不過,在並行情境中,還需要其他設備來同步化彼此之間各自獨立的控制緒。

非同步互動最後不一定要會合,但仍然需要其他設備來支援兩個控制緒的通訊。這種設備通常是具有訊息佇列的通訊通道,可以非同步地傳送和接收訊息。

請注意,單一應用程式可能混合著同步和非同步通訊,這取決於當訊息接收端處理訊息時,是否需要等待回應或可以接著處理其他工作。

切記,只有在可並行執行流程或執行緒的多重處理器上,才有可能實現真正的並行流程或執行緒;在單一處理器上,則是由作業系統排程器模仿出緒或流程同時執行的假象,排程器會將可用的處理資源分割為更小的片段,呈現多個執行緒或流程好像同時執行一樣。設計不良會破壞這樣的時間分割性;由於建立太多個頻繁同步通訊的流程或執行緒,導致流程或執行緒必須浪費更多「時間片段」才能迅速暫停和等待另一個流程或執行緒的回應。

共用資源的競爭

並行活動可能仰賴於必須共用的稀有資源。I/O 裝置就是常見的例子。如果一個活動需要的資源正由另一個活動使用,則必須等待輪到自己使用的機會。

競爭狀況:一致性狀態的問題

並行系統設計最基本的問題可能就在於如何避免「競爭狀況」。當部分系統中必須執行依狀態而定的功能時 (亦即,結果視系統現行狀態而定的功能),則必須確保此狀態在作業期間保持穩定。換句話說,某些作業絕對「不可分割」。每當兩個以上的控制緒可存取相同的狀態資訊時,需要有某種「並行控制」,以確保一個執行緒不會在另一個執行緒執行不可分割的狀態相依作業時修改狀態。試圖同時存取相同的狀態資訊,導致資訊在內部出現不一致的情形,就稱為「競爭狀況」。

在電梯系統中,當乘客選擇樓層時,就很容易出現典型的「競爭狀況」。電梯根據停靠樓層清單上下往返。每當電梯到達一個樓層時,一個控制緒會從適當的清單中移除此樓層,並從清單中取得下一個目標樓層。如果清單空白,但另一份清單含有樓層,電梯會改變方向,如果兩份清單同時空白,電梯會開始閒置。當乘客選擇樓層時,另一個控制緒負責將樓層要求放入適當的清單中。每一個執行緒會在清單上執行作業組合(這些組合原本是可分割的):例如,檢查下一個可用的空位,然後填寫空位。如果執行緒的作業碰巧發生交錯,則很容易在清單中覆寫相同的位置。

死結

死結是指兩個控制緒互相阻擋的狀況,彼此等待對方採取某些動作。很諷刺的是,我們為了防止競爭狀況而套用的某些同步化機制,卻往往成為導致死結發生的原因。

電梯的競爭狀況例子很容易形成一個相對良性的死結案例。電梯控制緒認為清單空白,因此不會啟動前往其他樓層。樓層要求執行緒則認為電梯正在清空清單,因此也不會主動通知電梯改變閒置狀態。

其他情境問題

除了「基本」問題,設計並行軟體時還有些情境問題必須確實解決。

效能取捨

在單一 CPU 內,透過切換作業來模擬並行性所需要的機制可能會佔用掉原本可供執行應用程式使用的 CPU 週期資源。另一方面,如果軟體必須等待 I/O 裝置,則並行性效能提升的價值,可能遠比其他任何額外的負荷更划得來。

複雜性取捨

並行軟體需要協調和控制機制,這些機制在序列化程式設計應用程式中沒有必要。這些機制讓並行軟體更複雜,且增加出錯的機會。因為多控制緒的緣故,並行系統的問題天生就難以診斷。另一方面,誠如我們稍早指出,當外部動因本身就具有並行性時,採獨立方式處理不同事件的並行軟體,可能遠比必須應付隨機事件的序列化程式更簡單。

不確定性

因為決定並行元件如何交錯執行的因素眾多,同一個軟體也可能以不同順序來回應相同的事件序列。根據設計而定,順序的改變可能產生不同的結果。

應用軟體在並行控制中扮演的角色

應用軟體並不一定會涉及並行控制的實作。涉及的可能性不勝枚舉,依涉及程度由低到高列舉如下:

  1. 作業系統可能隨時中斷應用程式作業(先佔式多工)。
  2. 應用程式作業可能定義不可分割的處理單位(主要區段),不可中斷,且在進入和離開這種處理單位時必須通知作業系統。
  3. 應用程式作業可能決定何時將 CPU 控制權交給其他作業(調協式多工)。
  4. 應用軟體可能必須全權負責排定和控制執行各項作業。

這些責任並非互不可分,亦不互斥。在一個給定的系統中,可能會同時混搭數種實作方式。

並行性抽象化

在設計過程中,太早選擇用於並行性的特定機制是並行系統設計中常犯的錯誤。每一種機制各有利弊,通常是經過詳細權衡與折衷之後,才找出適合特定狀況的「最佳」機制。愈早選擇機制,可憑藉的資訊就愈少。敲定機制也很容易降低設計在因應不同狀況時的彈性和調整性。

以最複雜的設計作業而言,經過多層的抽象最能確實瞭解並行性。首先,必須根據期望的行為,確實掌握系統的功能需求。接著,應該探索並行性可能的角色。最好運用執行緒的抽象化,不要仰賴特定的實作。在可能範圍內,應儘可能保留並行性實現機制的最終改選機會,以允許微調效能及根據不同產品配置以不同方式分散元件的彈性。

問題領域(例如電梯系統)和解決方案領域(軟體建構)之間的「概念距離」,仍然是系統設計中最大的難題之一。在瞭解和傳達複雜理念(例如並行行為)及填補概念落差方面,「視覺形式主義」相當有用。已證實有助於解決此類問題的工具包括:

  • 模組圖,構想並行執行的元件;
  • 時間緒,構想並行和互動的活動(可能與元件正相交);
  • 序列圖,呈現元件的互動;
  • 狀態轉移圖,定義元件的狀態和依狀態而定的行為。

做為並行元件的物件

若要設計並行軟體系統,我們必須結合軟體建置區塊(程序和資料結構)與並行性建置區塊(控制緒)。我們已討論過並行活動的概念,但我們無法從活動來建構系統。系統是以元件來建構,以並行元件來建構並行系統,聽起來也很合理。就程序、資料結構及控制緒本身而言,皆無法為並行元件打造一個很自然的模型,但物件似乎可以很自然地將這些必要元素全部打包起來,形成一個簡潔的套件。

物件將程序和資料結構包裝在一個緊密的元件中,具有自己的狀態和行為。其中封裝狀態和行為的特定實作方式,並定義介面,做為與其他物件或軟體互動的管道。物件通常模擬真實世界的實體或概念,且透過交換訊息的方式,與其他物件發生互動。現在已普遍成為建構複雜系統的最佳方式。

圖解說明詳見下文。

圖 4:  電梯系統的一組簡單的物件。


請思考電梯系統的物件模型。每一樓的監控站物件會監視該樓層的上樓和下樓按鈕。當一位潛在乘客按下按鈕時,監控站物件會開始回應,將訊息傳送至電梯調度器物件,此物件會選擇能最快提供服務的電梯、調度電梯並確認呼叫。每一個電梯物件會同時和獨立地控制其相對的實體電梯成物件,包括回應乘客的樓層選擇及調度器的呼叫。

在這種物件模型中,並行性有兩種形式。兩個以上的物件透過個別控制緒來獨立執行活動時,即形成物件間並行性。單一物件中有多個控制緒同時執行時,即形成物件內並行性。現今,在大多數的物件導向語言中,物件都是「被動的」,本身沒有控制緒。必須由外部環境提供控制緒。 環境通常是一個標準的 OS 流程,設計來執行以 C++ 或 Smalltalk 等語言撰寫的物件導向「程式」。如果 OS 支援多重執行緒作業,則多個主動式的執行緒可能分散在相同或不同的物件中。

在下圖中,被動物件以環形元素表示。每一個物件內部的灰色區域代表狀態資訊,外部的分節環狀代表程序(方法),這些程序定義物件的行為。

圖解說明詳見下文。
圖 5:  物件互動的圖例。

物件內並行性本身就含括並行軟體的所有難題,例如,多個控制緒存取相同記憶體空間時 (在本例中,指物件封裝的資料)發生競爭狀況的可能性。有人或許認為資料封裝可以解決這個問題。但問題在於物件不會封裝控制緒。在大多數情況,物件間並行性可以避免這些問題,但仍然有一個棘手的問題。為了讓兩個物件經由交換訊息來發生互動,至少要有兩個控制緒來處理訊息,並存取相同的記憶體空間來交換訊息。有一個相關(但仍然很困難)的問題在於物件分散於不同的流程,甚至是不同的處理器。在不同流程中,物件之間的訊息傳遞需要跨流程通訊的支援,期間往往涉及需將訊息編碼和解碼成可跨越流程界限的資料過程。

這些問題當然都有解決之道。事實上,誠如我們在先前章節中指出,每一個並行系統都會遇到這些問題,因此自然會發展出其各自施行的解決方案。只是「並行控制」會引起額外的工作和增加出錯的機率。再者,也會導致應用程式問題的本質混淆不清。基於上述種種理由,我們希望應用程式設計師儘可能不必親自處理。解決方式之一建置物件導向的環境,支援並行物件之間的訊息傳遞(包括並行控制),且在單一物件內儘量或完全不使用多個控制緒。事實上,這樣會隨著資料一起封裝控制緒。

主動物件模型

本身具有控制緒的物件稱為「主動物件」。為了支援與其他主動物件的非同步通訊,每一個主動物件各有一個訊息佇列或「信箱」。建立物件時,環境會提供控制緒給物件,物件會封裝此控制緒,直到物件消失為止。在外界傳來訊息之前,主動物件會一直閒置,就像被動物件一樣。物件會執行任何適當的程式碼來處理訊息。正值物件繁忙時刻送來的任何訊息會放入信箱中等候處理。物件處理完一個訊息時,將返回信箱挑選下一個等候處理的訊息,或等待新訊息的到來。在電梯系統中,適合成為主動物件的候選物件包括電梯本身、每一樓的監控站及調度器。

主動物件也可以設計得相當有效率,端視實作方式而定。但產生的額外負荷的確高於被動物件。因此,由於並非每一項作業都需要並行執行,同一個系統中通常混有主動物件和被動物件。因為通訊方式有所不同,很難相互比較,但主動物件為被動物件提供一個理想的環境,可取代我們稍早使用的 OS 流程。事實上,如果主動物件將所有工作委派給被動物件,則基本上就等同於具有跨流程通訊機能的 OS 流程或執行緒。但更有趣的是,主動物件有自己的行為可處理部份工作,也會將其他部份委派給被動物件。

圖解說明詳見下文。

圖 6:  「主動式」物件為被動類別提供環境

在主動式的電梯物件內,適合成為被動物件的候選物件包括電梯上樓時必須停靠的樓層清單,以及下樓時必須停靠的另一份清單。電梯必須能夠在清單中查閱下一個要停靠的樓層、在清單中加入新的停靠樓層,以及移除已停靠過的樓層。

因為複雜的系統幾乎一律由深及數層的子系統所建構,且深入數層才會到達葉層次元件,所以,很自然可以延伸主動物件模型,允許主動物件中再包含其他主動物件。

雖然單執行緒主動物件不支援真正的物件內並行性,但在許多應用程式中,將工作委派給內含的主動物件,不失為一項合理的替代方案。這樣仍然保有完整封裝的主要優點,包括封裝每個物件的狀態、行為及控制緒,因此可以簡化並行控制的問題。

圖解說明詳見下文。

圖 7:  顯示巢狀主動物件的電梯系統

請思考上述的局部電梯系統。每一部電梯都有電梯門、升降機及控制面板。每一個元件都經由並行主動物件來適當模擬,其中,電梯門物件控制電梯門的開和關、升降機物件透過機械升降機來控制電梯的位置、控制面板物件監視樓層選擇按鈕及電梯門開/關按鈕。相較於由單一控制緒來管理所有行為,將並行控制緒封裝成主動物件可以建立更簡單的軟體。

物件的「一致性狀態」問題

我們在討論競爭狀況曾經說過,為了讓系統以正確和預期的方式運作,有些依狀態而定的作業絕對不可分割。

為了讓物件正常運作,在處理任何訊息的前後,狀態在內部一定要保持一致。在處理訊息期間,物件的狀態可能處於暫時性狀態,且因為作業可能只完成一部份,狀態可能混沌不明。

如果物件一律先完成某一個訊息的回應之後,才會再回應另一個訊息,則不會有暫時性狀態的問題。中斷一個物件來執行另一個物件也不會有問題,因為每一個物件會嚴密封裝本身的狀態。(嚴格說起來,這並不完全成立,稍後即將說明。)

在任何情況下,如果一個物件中斷處理某個訊息,而去處理另一個訊息,將可能發生競爭狀況,因此需要用到並行控制。但這又轉而提高發生死結的可能性。

因此,如果物件將一個訊息處理完畢之後才會接受下一個訊息,則並行設計通常很簡單。在我們提供的特定形式的主動物件模型中,已隱含這種行為。

在並行系統中,一致性狀態的問題本身可能以兩種不同的形式出現;以物件導向的並行系統而言,也許更容易理解這些形式。我們已討論過第一種形式,也就是如果多個控制緒可存取單一物件(被動或主動式)的狀態,則必須由 CPU 基本作業固有的不可分割性或並行控制機制來保護不可分割的作業。

一致性狀態問題的第二種形式可能較難以捉摸。如果多個物件(主動式或被動)包含相同的狀態資訊,則至少在很短的期間內,物件將無可避免地對狀態持有不同意見。在設計不良的情況下,物件可能長時間意見不同,甚至永遠無法達成一致。 不一致狀態的呈現可視為另一種形式的數學「對偶」。

例如,電梯運轉控制系統(升降機)必須保證電梯門已關閉不能開啟,才能移動電梯。如果設計的防護措施不當,可能會發生在電梯開始移動時因偵測到乘客觸碰開門按鈕而竟把電梯門打開的意外情況。

乍看之下,只允許狀態資訊存在一個物件中,似乎就可輕易解決這個問題。雖然可能有用,但也可能損及效能,這在分散式系統中尤其明顯。再者,這並不是一個十分安全的解決方案。即使只有一個物件包含特定的狀態資訊,一旦其他並行物件在特定時刻根據此狀態來做決策,則狀態改變將導致其他物件的決策失效。

一致性狀態的問題沒有萬靈丹。所有情境解決方案都需要找出不可分割的作業,然後以某種同步化機制來保護這些作業,防止極短時間內的並行存取。「極短」可謂完全視情況而定。可能是像 CPU 儲存浮點數的所有位元組所花費的時間,或搭乘電梯到下一個停靠樓層所花費的時間。

即時系統

在即時系統中,RUP 建議採用 封裝體來代表主動物件。封裝體有強制規定的語意可簡化並行性建模:
  • 採用非同步訊息的通訊,透過 且使用正確定義的 通訊協定
  • 採用徹底完成的語意來處理訊息;
  • 封裝被動物件(保證不會發生執行緒干擾)。