1. 程式人生 > >(轉)C++程序間和執行緒間通訊

(轉)C++程序間和執行緒間通訊



1.許多程式和應用一起工作達到某個共同目的的任務集。每個任務在開始執行前等待前一個任務完成。為了完成共同目標,相關執行緒或程序必須相互合作與通訊。

2.依賴關係:對於任意兩個執行緒或程序,存在4種依賴關係(如圖)

C++多執行緒程式設計(5)程序間和執行緒間通訊
 (1)通訊依賴性:當執行緒A需要來自執行緒B的資料進行操作時(單向依賴)
 (2)合作依賴性:當執行緒A需要執行緒B擁有的資源,而且線上程A可以使用這些資源前,執行緒B必須釋放它。如count計數:findFiles()搜尋列表中的檔案,搜尋一個檔案count+1,findWords()搜尋檔案中的單詞列表,搜到一個檔案後count-1,兩者必須合作完成。
 (3)計數執行緒與程序依賴性:依賴性圖僅對少量執行緒或程序有用。
 
3.程序間和執行緒間通訊:
 2種技術實現程序或執行緒的共同目標:
  (1)在具有通訊依賴關係

的兩個程序間傳遞資訊。
  (2)同步。當執行緒或程序相互間具有合作依賴性時使用。
 

4.程序間通訊:資料對於其他程序來說是受保護的,為了讓一個程序訪問另一程序的資料,必須最終使用作業系統呼叫。當程序將資料傳送到另一個程序時,必須在程序間建立一種通訊方式IPC(程序間通訊)。
關聯(派生)程序和非關聯(非父子關係)程序實現通訊的技術
(1)關聯程序實現程序間通訊技術:繼承資源命令列引數
當建立一個子程序,它接收了父程序許多資源(如文字、堆疊以及資料片段)的拷貝。父程序可在資料片段或環境中設定變數,然後執行fork(),子程序接收這些值,同樣父程序定位一個檔案期望的位置,然後執行fork(),子程序就可以在父程序離開讀/寫指標的準確位置訪問該檔案。缺點:單向、一次性通訊。通訊像指揮棒傳遞。一旦父程序傳遞了某些資源的拷貝,子程序對他的使用就是獨立的,必須使用原始傳遞資源。另一種單向、一次性通訊是命令列引數,它是最簡單形式的程序間通訊。只限制於關聯程序。

(2)非關聯程序實現程序間通訊技術:管道(pipe).
 用於在關聯程序間以及無關聯程序間進行通訊。管道是一種像序列化檔案一樣訪問的資料結構。管道結構通過使用檔案和寫方式來訪問。如程序A希望傳送資料給程序B,那麼程序A向管道寫入資料,程序B必須讀取管道接收資料。管道可用於雙向通訊。寫入的資料和讀取的資料是由一系列位元組簡單表示。管道可以在程式的整個執行期間使用,在程序間傳送和接收資料。

兩種管道型別:a.匿名管道(關聯程序)——藉助於檔案描述符實現(子程序從父程序繼承了檔案描述符);

b.命名管道(非關聯程序)——具有某名字的特殊型別檔案,可以建立一個IPC客戶/伺服器模型,可以跨網路訪問命令管道。

命令管道相對於匿名管道的優點:命名管道可以被無關聯程序使用、命名管道可以持久、命名管道可以再網路或分佈環境中使用、命名管道容易用於多對一關係中。
兩者都是通過read()或write()函式來訪問。

WIN32環境命名管道工作所需的API呼叫:
CallNamePipe():連線到一個管道例項,然後寫入一條訊息、讀取訊息,並關閉管道控制代碼。只能被客戶程序和訊息管道使用
PeekNamedPipe():讀取通過管道傳輸的資料,而不會從位元組管道或訊息管道取出資料。返回管道例項的資訊。
DisconnectNamedPipe():客戶和程序使用完管道例項後,伺服器程序呼叫此函式關閉客戶程序的連線,客戶控制代碼變成無效,並拋棄管道中的所有未讀資料。
TransactNamedPipe():寫請求訊息並讀答覆訊息。如呼叫程序的管道控制代碼設定成訊息讀取模式,就可以與訊息管道一起使用。
FlushFileBuffers():伺服器程序呼叫該函式確保客戶程序讀取寫入管道的所有位元組和訊息。如果函式沒有返回到客戶程序,它就從管道讀取了所有的資料。
GetNamedPipeInfo():獲取命名管道的資訊。它返回管道的型別、輸入和輸出緩衝器的大小以及建立管道例項的最大數。
GetNamedPipeHandleState():提供控制代碼,包括管道的讀和等待模式、管道例項的當前數以及通過網路進行通訊的管道的其他所有重要資訊。
SetNamedPipeHandleState():設定管道控制代碼的讀和等待模式。它還控制蒐集位元組的最大數,或者客戶程序與遠端伺服器通訊時傳輸訊息前等待的最長時間。
ReadFile()、WriteFile():用於位元組和訊息管道。使用一個事件物件來通知其完成。

【注意】伺服器程式負責建立客戶與伺服器程序間管道通訊的協議。伺服器程式必須指定訪問模式、阻塞模式、管道型別、讀取模式、緩衝器大小及管道使用的即時計數。
 伺服器建立管道來發送和接收資料(如下圖)。伺服器制定這個管道將處於阻塞模式。如果請求read(),而且管道式空的,則read()阻塞,直到資料可以被讀取為止。如果請求的是管道的write(),且管道已滿,則write()阻塞,直到管道中騰出更多的空間放入資料為止。

命名管道常常用於多執行緒伺服器。

C++多執行緒程式設計(5)程序間和執行緒間通訊

 (3)共享記憶體:實現程序間通訊的一種方式。希望訪問該記憶體塊的其他程序必須請求對它的訪問,或由建立它的程序授予訪問記憶體塊的許可權。共享記憶體被對映到使用它的每個程序的地址空間。當一個程序寫共享記憶體,所有程序都立即知道寫入的內容,並且可以訪問。相當於函式間全域性變數的關係類似。程序可能共享一個邏輯地址,也可以共享某些實體地址。
通常使用共享記憶體比使用管道或佇列更簡單更有效。共享記憶體塊可用於儲存大資料結構。可用於對映檔案到記憶體,使得應用程式減輕了常規檔案訪問的I/O操作代價。

WIN32 環境建立和使用共享記憶體所需API:
CreateFileMapping() 建立一個檔案對映物件,對檔案無限制。
OpenFileMapping() 獲取對映物件的控制代碼
MapViewOfFile() 獲取共享記憶體的起始地址

 (4)動態資料交換(DDE):當今最強大最完善的程序間通訊形式之一。
使用訊息傳遞、共享記憶體、事務協議、客戶/伺服器範例、同步規則以及會話協議來讓資料和控制資訊在程序間流動。
  DDE基本模型:客戶/伺服器。一對多或多對一。DDE代理既可以是客戶,也可以是伺服器。程序可以從一個正為另一個程序執行服務的DDE代理請求服務。
  DDE互動的4種元件:DDE伺服器(應用、主題、項)、DDE客戶、DDE會話連結(互相傳遞的一系列訊息,熱連結和冷連結、5種主要型別的事務——請求、建議、取消建議、執行、傳送)、共享記憶體和DDE會話。
  DDE客戶和伺服器使用應用標識、主題、項以及共享記憶體的結合進行會話,一旦對主題-項對達到一致,就可以在客戶程序和伺服器程序間交換資料。
 

5.執行緒間通訊
  當兩個執行緒通訊時,一般使用屬於同一個程序的部分資料結構來實現。程序內的兩個執行緒可以訪問同一個資料片段,可以相互從堆疊片段傳遞值。在需要併發的多種程式設計場合,執行緒建立以及執行緒間通訊機制的效率比程序間通訊的效率高很多,使得執行緒成為更具吸引力的方案。
 執行緒間通訊型別:a.全域性資料、b.全域性變數、c.全域性資料結構 d.執行緒間通訊的引數 e.檔案控制代碼

 C++多執行緒程式設計(5)程序間和執行緒間通訊
[注意]如果多個執行緒用於寫入全域性資料結構,必須使用合作技術。

[程式]建立2個執行緒threadA、threadB,其中4個全域性變數(1個有理物件M和3個集迭代器A\B\C)和3個全域性資料結構(SetA,SetB,SetC)。 threadA()功能:將有理數插入SetA和SetB,使用set_intersection()求SetA和SetB的交集,交集結果為SetC。threadB()功能:SetA和SetB中插入有理數,使用set_union()求SetC和SetB的並集,並集結果為SetA。主執行緒pthead_jion()等待threadA()和threadB()完成,完成後遍歷SetA、SetB、SetC,將他們的成員傳送到標準輸出。

C++多執行緒程式設計(5)程序間和執行緒間通訊

C++多執行緒程式設計(5)程序間和執行緒間通訊

C++多執行緒程式設計(5)程序間和執行緒間通訊

【注意】在多執行緒環境中,同時訪問全域性變數和資料結構必須同步化(synchronized)。

6.執行緒間通訊的引數傳遞:

建立執行緒:pthread_create(ThreadId,NULL,threadFunction,(void *)X);
當使用執行緒建立API給一個執行緒傳遞指標時,該指標就像exec函式家族的引數一樣使用。exec函式家族用於建立子程序,幷包含作為形參傳遞給子程序的引數。

使用exec函式家族與使用執行緒建立API傳遞引數的區別:

exec函式中的引數表示一種單向通訊,子程序只複製在exec呼叫中傳遞的引數值。當執行緒接收到引數時,它並不是一個拷貝,而是某些資料位置的地址。對執行緒中資料的任何修改都將反應在建立該資料的程序中,這與在exec呼叫中子程序所接收的引數相反,子程序對引數所做的任何修改不會反映在父程序中。

[程式]演示執行緒引數的使用:

C++多執行緒程式設計(5)程序間和執行緒間通訊

注意:由threadA()和threadB()對N指標物件的任何修改都將反映到threadA()、threadB()、main()。因為所有的3個執行緒可能併發執行,所以不要修改儲存在N中的物件,除非修改是同步的。但在此程式中,儲存在 N 中的物件只用在讀或複製的場合,不必擔心資料競爭的發生。

【重點】將引數用作執行緒間通訊的一種形式可能較之將全域性變數或全域性資料結構用作執行緒間通訊機制更理想。通過監視那些作為引數接收資料的執行緒可以控制同步。

7.檔案控制代碼:

在多執行緒間的共享檔案作為一種執行緒的通訊形式時,要小心全域性變數。在多執行緒環境中,序列化或同步化檔案訪問時也要小心,必須使用同步和合作技術。