通常而言,同步變量和條件變量聲明為全局,所以,多個線程可以訪問它們。雖然這需要使用同步變量的所有線程提供了方便,但它沒有提供這些線程遵循的策略。這意味著線程可以按任意方式自由使用(或誤用)同步變量。而且,傳統(tǒng)同步變量沒有為程序員提供實際的方法,將變量與需要同步的數(shù)據(jù)或設(shè)備直接關(guān)聯(lián)。
隨著軟件工程變得復(fù)雜,單個程序中源模塊的數(shù)量逐漸增加。在大型多線程程序中,同步變量可能在一個源模塊中全局聲明,程序內(nèi)的眾多源模塊都可以訪問。跟蹤哪一個源模塊正鎖定或取消鎖定哪一個同步變量是一項繁瑣而且容易出錯的任務(wù)。而且,如果同步變量的數(shù)量很大,分清哪些變量與哪些數(shù)據(jù)對應(yīng)使用變得更困難。
使用面向?qū)ο蠹夹g(shù)去同步化變量。首先,互斥量或條件變量以及所有的相關(guān)系統(tǒng)服務(wù)都被封閉在一個接口類中。這意味著互斥量對線程內(nèi)的所有函數(shù)或過程不再具有全局性。對互斥量的唯一的訪問途徑是通過類成員函數(shù)。其次,接口類與即將通過繼承或復(fù)合同步的數(shù)據(jù)或設(shè)備直接關(guān)聯(lián)。
1. Mutext類
class mutex{
protected:
pthread_mutex_t Mutex;
general_exception SemException;
public:
mutex(void){
if(ptrhead_mutex_init(&Mutex, NULL)){
SemException.message("Could Not create Mutex");
throw SemException;
}
~mutex(void);
void lock(void);
void unlock(void);
};
使用mutex類與傳統(tǒng)互斥量方法的重要區(qū)別在于互斥量類型的對象不會聲明為自由漂浮或全局的對象。互斥量類型對象設(shè)計用作數(shù)據(jù)類或設(shè)備類的組件。它可以私有性地被一個數(shù)據(jù)或設(shè)備類繼承,也可以嵌入另一個使用復(fù)合的類中。
mutex用于保護(hù)該宿主類具有的所有臨界區(qū),該類決定部署互斥量的方式和時間。對象的用戶不必?fù)?dān)心同步化對象的使用,因為對象同步化自身。同步臨界區(qū)的責(zé)任從對象的用戶轉(zhuǎn)而由對象的生產(chǎn)者承擔(dān)。
1.1 命名互斥量類
命名互斥量可以被同一進(jìn)程內(nèi)的線程以及不同進(jìn)程間的線程共享,而匿名互斥量則只用于執(zhí)行同一進(jìn)程中線程間的同步,它不與文件名字關(guān)聯(lián)。在命名互斥量類中,我們可使用互斥量持續(xù)時間(mutex duration)作為一種技術(shù)來幫助我們避免死鎖。
class named_mutex:public mutex{
protected:
char MutexName[81];
int initiallyOwned;
public:
named_mutex(void);
named_mutex(char *MName, int Owned = 0);
unsigned long lockDuration(void);
void lockDuration(unsigned long Dur);
};
1.2 同步和依賴性關(guān)系
同步關(guān)系相關(guān)的控制機(jī)制為:互斥量、條件變量與條件斷言;依賴性關(guān)系相關(guān)的控制機(jī)制為:線程間通信和進(jìn)程間通信。
面向?qū)ο缶幊痰囊粋€基本概念即類通過方法(method)調(diào)控對數(shù)據(jù)組件的所有訪問。
我們的目標(biāo)是通過面向?qū)ο蠹夹g(shù)管理競爭條件和死鎖,同樣我們希望將實現(xiàn)并發(fā)的責(zé)任轉(zhuǎn)移到類、類庫以及應(yīng)用框架級別,減少并發(fā)和同步編程的一些復(fù)雜性。
1.3 表示條件的類
class event_mutex{
protected:
LPSECURITY_ATTRIBUTES SecurityPtr;
HANDLE EventMutex;
char MutexName[81];
int InitiallySet;
general_exception EventException;
unsigned long EventWait;
unsigned int AccessLevel;
int ManualReset;
public:
event_mutex(char *MName, unsigned int Initial = 0, unsigned long Reset,
LPSECURITY_ATTRUBUTES Secure){
EventMutex = CreateEvent(Secure, Reset, Initial, Mname);
if(!EventMutex){
EventException.message("Could not create Event Semaphore");
throw EventException;
}
if(MName !=NULL){
strcpy(MutextName, MName);
}
EventWait = INFINITE;
}
event_mutex(char *MName, unsigned int Access, unsigned long Inherit){
EventMutex = OpenEvent(Access, Inherit, MName);
if(!EventMutex){
EventException.message("Could not create Event Semaphore");
throw EventException;
}
if(MName != NULL){
strcpy(MutexName, Mname);
}
EventWait = INFINITE;
}
void postEvent(void){
SetEvent(EventMutex);
}
void waitEvent(void){
WaitForSingleObject(EventMutex, EventWait);
}
void resetEvent(void){
ResetEvent(EventMutex);
}
~event_mutex(void){
if(!CloseHandle(EventMutex)){
EventException.message("Could not close mutex");
throw EventException;
}
}
};
其中使用多構(gòu)造函數(shù)的模式使得客戶/服務(wù)器多進(jìn)程處理更方便,它避免程序員必須編寫重復(fù)代碼之勞。
1.4 等待多個事件或互斥量
另一種類型的條件變量就是包含多個事件或互斥量的事件互斥量(event mutex)。阻塞于這些多個等待變量的線程的線程可能等待所有事件,在處理過程繼續(xù)之前,互斥量根據(jù)這些事件來設(shè)置。
class multiple_event_mutex{
protected:
char MutexName[81];
SEMRECORD *ConditionList;
int EventNumber;
long int WaitSelection;
HEV EventMutex;
MultipleEventMutex;
general_exception EventException;
int Duration;
public:
multiple_event_mutex(char *MName, int ENum, long int WaitType, int Initial){
int N;
EventNumber = ENum;
ConditionList = new SEMRECORD[ENum];
for(N = 0; N < ENum; ++N){
if(DosCreateEventSem(NULL, &EventMutex, 0, Imitial)){
EventException.message("Could not create event semaphore");
throw EventException;
}
ConditionList[N].hsemCur = &EventMutex;
ConditionList[N].ulUser = N;
}
if(DosCreateMuxWaitSem(MName, &MultipleEventMutex, sizeof(ConditionList), ConditionList, WaitType)){
EventException.message("Could not create multiple event semaphore.");
throw EventException;
}
}
multiple_event_mutex(char *MName){
if(DosOpenMuxWaitSem(MName, &MultipleEventMutex)){
EventException.message("Could not open event semaphore");
throw EventException;
}
}
~multiple_event_mutex(void){
if(DosCloseMuxWaitSem(MultipleEventMutex)){
EventException.message("Could not close event semaphore");
throw EventException;
}
delete ConditionList;
}
int duration(int x){
return Duration;
}
void duration(int x){
Duration = X;
}
void postEvent(int x){
EventMutex = (HEV) ConditionList[x].hsemCur;
DosPostEventSem(EventMutex);
}
void waitEvents(void){
unsigned long user;
DosWaitMuxWaitSem(MultipleEventMutex, SEM_INDEFINITE_WAIT, &User);
}
};
針對復(fù)雜事務(wù)處理,有一套標(biāo)準(zhǔn)可以衡量特定事務(wù)是否讓數(shù)據(jù)庫處于一致的狀態(tài)。其中一個標(biāo)準(zhǔn)就是ACID測試。縮寫ACID代表原子性(atomicity)、一致性(consistency),孤立性(isolation)以及持久性(durability)。
};
1.5 小結(jié)
同步對象可以通過設(shè)計封裝了系統(tǒng)API的接口類(Inteface class)來創(chuàng)建。一旦創(chuàng)建了同步類(synchronization class),在那些被多線程或多進(jìn)程并發(fā)或同步使用的任何類型C++類中,可以添加以上同步類。在某個類中包含同步類或者使用復(fù)合、包容或繼承來實現(xiàn)。建議只讓類中的直接成員訪問同步類,只有在非常特定的條件下才允許同步類被派生類訪問。
在try塊中放入聲明對象的優(yōu)點在于,拋出異常時自動調(diào)用它們的析構(gòu)函數(shù)。應(yīng)該用面向?qū)ο蠹夹g(shù)來關(guān)聯(lián)同步機(jī)制與即將同步的對象或設(shè)備,也就是說在任何可能的地方,避免自由漂浮的同步對象。使用類聚集(class aggregation)來連接同步類(synchronization class)與數(shù)據(jù)(data class)和設(shè)備類(device class)。