同步和通訊的目的是一樣的,實現(xiàn)進程間數(shù)據(jù)共享,同步只是為了做到處理協(xié)同。
共享內(nèi)存在Win9X平臺上是有的,在NT內(nèi)核以后就沒有這一說了,因為進程地址空間不再有共用部分 Linux :通信就是說進程之間傳遞數(shù)據(jù)。常見的方法有 pipe(管道),F(xiàn)IFO(命名管道),socket(套接字),SysVIPC 的 shm(共享內(nèi)存)、msg queue(消息隊列),mmap(文件映射)。
以前還有 STREAM,不過現(xiàn)在比較少見了(好像)。 同步的意思是說,讓不同進程能夠在同時到達一個已知的特定狀態(tài)之前等待另一方的執(zhí)行。
Linux 下常見的同步方法有SysVIPC 的 sem(信號量)、file locking / record locking(通過 fcntl 設定的文件鎖、記錄鎖)、futex(基于共享內(nèi)存的快速用戶態(tài)互斥鎖)。針對線程(pthread)的還有 pthread_mutex 和 pthread_cond(條件變量)。
除了這些特定的同步對象之外,還有一些同步方法是與通信方法不可分離的,包括:對 pipe/FIFO/socket 和 msg queue 的阻塞等待、對子進程退出事件的等待(wait族)、對線程退出時間的等待(pthread_join) 另外還有一個不能不提的,就是信號。
進程間通信機制 1 文件映射 文件映射(Memory-Mapped Files)能使進程把文件內(nèi)容當作進程地址區(qū)間一塊內(nèi)存那樣來對待。
因此,進程不必使用文件I/O操作,只需簡單的指針操作就可讀取和修改文件的內(nèi)容。 Win32 API允許多個進程訪問同一文件映射對象,各個進程在它自己的地址空間里接收內(nèi)存的指針。
通過使用這些指針,不同進程就可以讀或修改文件的內(nèi)容,實現(xiàn)了對文件中數(shù)據(jù)的共享。 應用程序有三種方法來使多個進程共享一個文件映射對象。
(1)繼承:第一個進程建立文件映射對象,它的子進程繼承該對象的句柄。 (2)命名文件映射:第一個進程在建立文件映射對象時可以給該對象指定一個名字(可與文件名不同)。
第二個進程可通過這個名字打開此文件映射對象。另外,第一個進程也可以通過一些其它IPC機制(有名管道、郵件槽等)把名字傳給第二個進程。
(3)句柄復制:第一個進程建立文件映射對象,然后通過其它IPC機制(有名管道、郵件槽等)把對象句柄傳遞給第二個進程。第二個進程復制該句柄就取得對該文件映射對象的訪問權限。
文件映射是在多個進程間共享數(shù)據(jù)的非常有效方法,有較好的安全性。但文件映射只能用于本地機器的進程之間,不。
進程間通信機制 1 文件映射 文件映射(Memory-Mapped Files)能使進程把文件內(nèi)容當作進程地址區(qū)間一塊內(nèi)存那樣來對待。因此,進程不必使用文件I/O操作,只需簡單的指針操作就可讀取和修改文件的內(nèi)容。
Win32 API允許多個進程訪問同一文件映射對象,各個進程在它自己的地址空間里接收內(nèi)存的指針。通過使用這些指針,不同進程就可以讀或修改文件的內(nèi)容,實現(xiàn)了對文件中數(shù)據(jù)的共享。
應用程序有三種方法來使多個進程共享一個文件映射對象。 (1)繼承:第一個進程建立文件映射對象,它的子進程繼承該對象的句柄。
(2)命名文件映射:第一個進程在建立文件映射對象時可以給該對象指定一個名字(可與文件名不同)。第二個進程可通過這個名字打開此文件映射對象。
另外,第一個進程也可以通過一些其它IPC機制(有名管道、郵件槽等)把名字傳給第二個進程。 (3)句柄復制:第一個進程建立文件映射對象,然后通過其它IPC機制(有名管道、郵件槽等)把對象句柄傳遞給第二個進程。
第二個進程復制該句柄就取得對該文件映射對象的訪問權限。 文件映射是在多個進程間共享數(shù)據(jù)的非常有效方法,有較好的安全性。
但文件映射只能用于本地機器的進程之間,不能用于網(wǎng)絡中,而開發(fā)者還必須控制進程間的同步。 2 共享內(nèi)存 Win32 API中共享內(nèi)存(Shared Memory)實際就是文件映射的一種特殊情況。
進程在創(chuàng)建文件映射對象時用0xFFFFFFFF來代替文件句柄(HANDLE),就表示了對應的文件映射對象是從操作系統(tǒng)頁面文件訪問內(nèi)存,其它進程打開該文件映射對象就可以訪問該內(nèi)存塊。由于共享內(nèi)存是用文件映射實現(xiàn)的,所以它也有較好的安全性,也只能運行于同一計算機上的進程之間。
注意點: 要控制同步,而且CString、list、arry、map等的collect class都不能安全的使用于共享內(nèi)存中 不要把擁有虛函數(shù)之C++類放到共享內(nèi)存中 不要把CObject派生類之MFC對象放到共享內(nèi)存中 不要使用"point within the shared memory"的指針 不要使用"point outside of the shared memory"的指針 使用"based"指針是安全的,但要小心使用 3 匿名管道 管道(Pipe)是一種具有兩個端點的通信通道:有一端句柄的進程可以和有另一端句柄的進程通信。管道可以是單向-一端是只讀的,另一端點是只寫的;也可以是雙向的一管道的兩端點既可讀也可寫。
匿名管道(Anonymous Pipe)是 在父進程和子進程之間,或同一父進程的兩個子進程之間傳輸數(shù)據(jù)的無名字的單向管道。通常由父進程創(chuàng)建管道,然后由要通信的子進程繼承通道的讀端點句柄或?qū)?端點句柄,然后實現(xiàn)通信。
父進程還可以建立兩個或更多個繼承匿名管道讀和寫句柄的子進程。這些子進程可以使用管道直接通信,不需要通過父進程。
匿名管道是單機上實現(xiàn)子進程標準I/O重定向的有效方法,它不能在網(wǎng)上使用,也不能用于兩個不相關的進程之間。 4 命名管道 命名管道(Named Pipe)是服務器進程和一個或多個客戶進程之間通信的單向或雙向管道。
不同于匿名管道的是命名管道可以在不相關的進程之間和不同計算機之間使用,服務器建立命名管道時給它指定一個名字,任何進程都可以通過該名字打開管道的另一端,根據(jù)給定的權限和服務器進程通信。 命名管道提供了相對簡單的編程接口,使通過網(wǎng)絡傳輸數(shù)據(jù)并不比同一計算機上兩進程之間通信更困難,不過如果要同時和多個進程通信它就力不從心了。
5 郵件槽 郵件槽(Mailslots)提 供進程間單向通信能力,任何進程都能建立郵件槽成為郵件槽服務器。其它進程,稱為郵件槽客戶,可以通過郵件槽的名字給郵件槽服務器進程發(fā)送消息。
進來的消 息一直放在郵件槽中,直到服務器進程讀取它為止。一個進程既可以是郵件槽服務器也可以是郵件槽客戶,因此可建立多個郵件槽實現(xiàn)進程間的雙向通信。
通過郵件槽可以給本地計算機上的郵件槽、其它計算機上的郵件槽或指定網(wǎng)絡區(qū)域中所。
現(xiàn)在最常用的進程間通信的方式有:信號,信號量,消息隊列,共享內(nèi)存。
所謂進程通信,就是不同進程之間進行一些"接觸",這種接觸有簡單,也有復雜。機制不同,復雜度也不一樣。通信是一個廣義上的意義,不僅僅指傳遞一些massege。
他們的使用方法是基本相同的,所以只要掌握了一種的使用方法,然后記住其他的使用方法就可以了。
1. 信號
在我學習的內(nèi)容中,主要接觸了信號來實現(xiàn)同步的機制,據(jù)說信號也可以用來做其它的事情,但是我還不知道做什么。
信號和信號量是不同的,他們雖然都可用來實現(xiàn)同步和互斥,但前者是使用信號處理器來進行的,后者是使用P,V操作來實現(xiàn)的。
使用信號要先知道有哪些信號,在Linux下有31個需要記住的通用信號,據(jù)說也是systemV中最常用的那些。這里略。
1. 1信號相關函數(shù):
#include
int sigaction(int signo, const struct sigaction *act, struct sigaction
*oact);
該函數(shù)用來為進程安裝信號處理器,struct sigaction數(shù)據(jù)是用來保存信號處理器的相關信息。
#include
int sigemptyset(sigset_t *set);
將信號集合清空。
int sigfillset(sigset_t *set);
將信號集合設置成包含所有的信號。在對信號進行操作以前一定要對信號集進行初始化。
int sigaddset(sigset_t *set, int signo);
向信號集中加入signo對應的新信號。
int sigdelset(sigset_t *set, int signo);
從信號集中刪除signo對應的一個信號。
int sigismember(const sigset_t *set, int signo);
判斷某個信號是否在信號集中。返回1則在,0則不在。
#include
int sigprocmask(int how,const sigset_t *set, sigset_t *oset);用來設置進程的信號屏蔽碼。信號屏蔽碼可以用來在某段時間內(nèi)阻塞一些信號集中的信號,如果信號不在信號集中,就不必討論它,因為肯定不響應,是否能生成也不肯定,我沒有做過試驗。
1.2我所理解的使用信號機制的方法:
使用信號,主要做的事情就是信號處理器的工作,這里面是你想做的事情。就像中斷處理函數(shù)一樣。
在使用信號以前,首先要初始化信號集,只有在信號集里面的信號才會被考慮。
有兩種方法可以初始化信號集,一種是設置空信號集,一種是將所有的信號都加到信號集中。如果你自己想要的信號集不是這兩種,可以在初始化了以后通過添加和刪除信號進行定制。
如果在進程執(zhí)行的一段時間內(nèi)不想對某些信號進行響應,則可以使用sigprocmask對當前的信號集中的一些信號進行阻塞,稍后再執(zhí)行。
用于進程間通訊(IPC)的四種不同技術:
1. 消息傳遞(管道,F(xiàn)IFO,posix和system v消息隊列)
2. 同步(互斥鎖,條件變量,讀寫鎖,文件和記錄鎖,Posix和System V信號燈)
3. 共享內(nèi)存區(qū)(匿名共享內(nèi)存區(qū),有名Posix共享內(nèi)存區(qū),有名System V共享內(nèi)存區(qū))
4. 過程調(diào)用(Solaris門,Sun RPC)
消息隊列和過程調(diào)用往往單獨使用,也就是說它們通常提供了自己的同步機制.相反,共享內(nèi)存區(qū)通常需要由應用程序提供的某種同步形式才能正常工作.解決某個特定問題應使用哪種IPC不存在簡單的判定,應該逐漸熟悉各種IPC形式提供的機制,然后根據(jù)特定應用的要求比較它們的特性.
必須考慮的四個前提:
1. 聯(lián)網(wǎng)的還是非聯(lián)網(wǎng)的.IPC適用于單臺主機上的進程或線程間的.如果應用程序有可能分布到多臺主機上,那就要考慮使用套接字代替IPC,從而簡化以后向聯(lián)網(wǎng)的應用程序轉(zhuǎn)移的工作.
2. 可移植性.
3. 性能,在具體的開發(fā)環(huán)境下運行測試程序,比較幾種IPC的性能差異.
4. 實時調(diào)度.如果需要這一特性,而且所用的系統(tǒng)也支持posix實時調(diào)度選項,那就考慮使用Posix的消息傳遞和同步函數(shù).
各種IPC之間的一些主要差異:
1. 管道和FIFO是字節(jié)流,沒有消息邊界.Posix消息和System V消息則有從發(fā)送者向接受者維護的記錄邊界(eg:TCP是沒有記錄邊界的字節(jié)流,UDP則提供具有記錄邊界的消息).
2. 當有一個消息放置到一個空隊列中時,Posix消息隊列可向一個進程發(fā)送一個信號,或者啟動一個新的線程.System V則不提供類似的通知形式.
3. 管道和FIFO的數(shù)據(jù)字節(jié)是先進先出的.Posix消息和System V消息具有由發(fā)送者賦予的優(yōu)先級.從一個Posix消息隊列讀出時,首先返回的總是優(yōu)先級最高的消息.從一個System V消息隊列讀出時,讀出者可以要求想要的任意優(yōu)先級的消息.
4. 在眾多的消息傳遞技術—管道,F(xiàn)IFO,Posix消息隊列和System V消息隊列—中,可從一個信號處理程序中調(diào)用的函數(shù)只有read和write(適用于管道和FIFO).
比較不同形式的消息傳遞時,我們感興趣的有兩種測量尺度:
1. 帶寬(bandwidth):數(shù)據(jù)通過IPC通道轉(zhuǎn)移的速度.為測量該值,我們從一個進程向另一個進程發(fā)送大量數(shù)據(jù)(幾百萬字節(jié)).我們還給不同大小的I/O操作(例如管道和FIFO的write和read操作)測量該值,期待發(fā)現(xiàn)帶寬隨每個I/O操作的數(shù)據(jù)量的增長而增長的規(guī)律.
2. 延遲(latency):一個小的IPC消息從一個進程到令一個進程再返回來所花的時間.我們測量的是只有一個1個字節(jié)的消息從一個進程到令一個進程再回來的時間(往返時間)
在現(xiàn)實世界中,帶寬告訴我們大塊數(shù)據(jù)通過一個IPC通道發(fā)送出去需花多長時間,然而IPC也用于傳遞小的控制信息,系統(tǒng)處理這些小消息所需的時間就由延遲提供.這兩個數(shù)都很重要.
什么是系統(tǒng)進程
進程是指在系統(tǒng)中正在運行的一個應用程序;線程是系統(tǒng)分配處理器時間資源的基本單元,或者說進程之內(nèi)獨立執(zhí)行的一個單元。對于操作系統(tǒng)而言,其調(diào)度單元是線程。一個進程至少包括一個線程,通常將該線程稱為主線程。一個進程從主線程的執(zhí)行開始進而創(chuàng)建一個或多個附加線程,就是所謂基于多線程的多任務。
那進程與線程的區(qū)別到底是什么?進程是執(zhí)行程序的實例。例如,當你運行記事本程序(Nodepad)時,你就創(chuàng)建了一個用來容納組成 Notepad.exe的代碼及其所需調(diào)用動態(tài)鏈接庫的進程。每個進程均運行在其專用且受保護的地址空間內(nèi)。因此,如果你同時運行記事本的兩個拷貝,該程序正在使用的數(shù)據(jù)在各自實例中是彼此獨立的。在記事本的一個拷貝中將無法看到該程序的第二個實例打開的數(shù)據(jù)。
以沙箱為例進行闡述。一個進程就好比一個沙箱。線程就如同沙箱中的孩子們。孩子們在沙箱子中跑來跑去,并且可能將沙子攘到別的孩子眼中,他們會互相踢打或撕咬。但是,這些沙箱略有不同之處就在于每個沙箱完全由墻壁和頂棚封閉起來,無論箱中的孩子如何狠命地攘沙,他們也不會影響到其它沙箱中的其他孩子。因此,每個進程就象一個被保護起來的沙箱。未經(jīng)許可,無人可以進出。
實際上線程運行而進程不運行。兩個進程彼此獲得專用數(shù)據(jù)或內(nèi)存的唯一途徑就是通過協(xié)議來共享內(nèi)存塊。這是一種協(xié)作策略。下面讓我們分析一下任務管理器里的進程選項卡。
這里的進程是指一系列進程,這些進程是由它們所運行的可執(zhí)行程序?qū)嵗齺碜R別的,這就是進程選項卡中的第一列給出了映射名稱的原因。請注意,這里并沒有進程名稱列。進程并不擁有獨立于其所歸屬實例的映射名稱。換言之,如果你運行5個記事本拷貝,你將會看到5個稱為Notepad.exe的進程。它們是如何彼此區(qū)別的呢?其中一種方式是通過它們的進程ID,因為每個進程都擁有其獨一無二的編碼。該進程ID由Windows NT或Windows 2000生成,并可以循環(huán)使用。因此,進程ID將不會越編越大,它們能夠得到循環(huán)利用。第三列是被進程中的線程所占用的CPU時間百分比。它不是CPU的編號,而是被進程占用的CPU時間百分比。此時我的系統(tǒng)基本上是空閑的。盡管系統(tǒng)看上去每一秒左右都只使用一小部分CPU時間,但該系統(tǒng)空閑進程仍舊耗用了大約99%的CPU時間。
第四列,CPU時間,是CPU被進程中的線程累計占用的小時、分鐘及秒數(shù)。請注意,我對進程中的線程使用占用一詞。這并不一定意味著那就是進程已耗用的CPU時間總和,因為,如我們一會兒將看到的,NT計時的方式是,當特定的時鐘間隔激發(fā)時,無論誰恰巧處于當前的線程中,它都將計算到CPU周期之內(nèi)。通常情況下,在大多數(shù)NT系統(tǒng)中,時鐘以10毫秒的間隔運行。每10毫秒NT的心臟就跳動一下。有一些驅(qū)動程序代碼片段運行并顯示誰是當前的線程。讓我們將CPU時間的最后10毫秒記在它的帳上。因此,如果一個線程開始運行,并在持續(xù)運行8毫秒后完成,接著,第二個線程開始運行并持續(xù)了2毫秒,這時,時鐘激發(fā),請猜一猜這整整10毫秒的時鐘周期到底記在了哪個線程的帳上?答案是第二個線程。因此,NT中存在一些固有的不準確性,而NT恰是以這種方式進行計時,實際情況也如是,大多數(shù)32位操作系統(tǒng)中都存在一個基于間隔的計時機制。請記住這一點,因為,有時當你觀察線程所耗用的CPU總和時,會出現(xiàn)盡管該線程或許看上去已運行過數(shù)十萬次,但其CPU時間占用量卻可能是零或非常短暫的現(xiàn)象,那么,上述解釋便是原因所在。上述也就是我們在任務管理器的進程選項卡中所能看到的基本信息列。
1。同步代碼塊:
synchronized(同一個數(shù)據(jù)){} 同一個數(shù)據(jù):就是N條線程同時訪問一個數(shù)據(jù)。
2。
同步方法:
public synchronized 數(shù)據(jù)返回類型 方法名(){}
就
是使用 synchronized 來修飾某個方法,則該方法稱為同步方法。對于同步方法而言,無需顯示指定同步監(jiān)視器,同步方法的同步監(jiān)視器是
this
也就是該對象的本身(這里指的對象本身有點含糊,其實就是調(diào)用該同步方法的對象)通過使用同步方法,可非常方便的將某類變成線程安全的類,具有如下特征:
1,該類的對象可以被多個線程安全的訪問。
2,每個線程調(diào)用該對象的任意方法之后,都將得到正確的結(jié)果。
3,每個線程調(diào)用該對象的任意方法之后,該對象狀態(tài)依然保持合理狀態(tài)。
注:synchronized關鍵字可以修飾方法,也可以修飾代碼塊,但不能修飾構造器,屬性等。
實現(xiàn)同步機制注意以下幾點: 安全性高,性能低,在多線程用。性能高,安全性低,在單線程用。
1,不要對線程安全類的所有方法都進行同步,只對那些會改變共享資源方法的進行同步。
2,如果可變類有兩種運行環(huán)境,當線程環(huán)境和多線程環(huán)境則應該為該可變類提供兩種版本:線程安全版本和線程不安全版本(沒有同步方法和同步塊)。在單線程中環(huán)境中,使用線程不安全版本以保證性能,在多線程中使用線程安全版本.
線程通訊:
為什么要使用線程通訊?
當
使用synchronized
來修飾某個共享資源時(分同步代碼塊和同步方法兩種情況),當某個線程獲得共享資源的鎖后就可以執(zhí)行相應的代碼段,直到該線程運行完該代碼段后才釋放對該
共享資源的鎖,讓其他線程有機會執(zhí)行對該共享資源的修改。當某個線程占有某個共享資源的鎖時,如果另外一個線程也想獲得這把鎖運行就需要使用wait()
和notify()/notifyAll()方法來進行線程通訊了。
Java.lang.object 里的三個方法wait() notify() notifyAll()
wait方法導致當前線程等待,直到其他線程調(diào)用同步監(jiān)視器的notify方法或notifyAll方法來喚醒該線程。
wait(mills)方法
都是等待指定時間后自動蘇醒,調(diào)用wait方法的當前線程會釋放該同步監(jiān)視器的鎖定,可以不用notify或notifyAll方法把它喚醒。
notify()
喚醒在同步監(jiān)視器上等待的單個線程,如果所有線程都在同步監(jiān)視器上等待,則會選擇喚醒其中一個線程,選擇是任意性的,只有當前線程放棄對該同步監(jiān)視器的鎖定后,也就是使用wait方法后,才可以執(zhí)行被喚醒的線程。
notifyAll()方法
喚醒在同步監(jiān)視器上等待的所有的線程。只用當前線程放棄對該同步監(jiān)視器的鎖定后,才可以執(zhí)行被喚醒的線程
lz你好,
在操作系統(tǒng)中,有很多術語都是想通的,都是相似的,相近的。比如同步、異步、并行、并發(fā)、互斥等等。對這類詞語,如果沒有同時出現(xiàn),我們對它們最好的處理就是不去比較它們,因為很多都沒有可比性。
首先:互斥和同步如果是同時出現(xiàn)的話,那就是相反的,值得比較的兩個術語。
【互斥】:就是說兩個進程只能在某一時刻執(zhí)行一個,這種結(jié)果可能是因為共同爭奪資源而產(chǎn)生的。舉個例子:假設把火車上的公共廁所看成一種臨界資源,而兩個乘客是兩個進程,我們就可以認為同時需要使用公共廁所的乘客是互斥的
【同步】:就是進程之間可以同時運行的,之間并不存在“利益沖突”,不競爭資源。大有“你走你的陽關道,我過我的獨木橋”之意,兩個進程互不干涉,互不影響。
說的很直白了,希望可以幫你o(∩_∩)o
聲明:本網(wǎng)站尊重并保護知識產(chǎn)權,根據(jù)《信息網(wǎng)絡傳播權保護條例》,如果我們轉(zhuǎn)載的作品侵犯了您的權利,請在一個月內(nèi)通知我們,我們會及時刪除。
蜀ICP備2020033479號-4 Copyright ? 2016 學習鳥. 頁面生成時間:2.956秒