線程同步和服務(wù)器數(shù)據(jù)保護(hù)
最近因為自己主持的項目出現(xiàn)些問題,太忙了,所以好久都沒有繼續(xù)寫東西和大家進(jìn)行探討制作開發(fā)部分了。在這一節(jié)中就要向大家介紹另外一個重要的部分,并且也是最頭疼的部分:線程同步和數(shù)據(jù)保護(hù)。
關(guān)于線程的概念我在前面的章節(jié)中已經(jīng)介紹過了,也就在這里不累贅—“重復(fù)再重復(fù)”了。有一定線程基礎(chǔ)的人都知道,線程只要創(chuàng)建后就如同脫韁的野馬,對于這樣的一匹野馬我們怎么來進(jìn)行控制和處理呢?簡單的說,我們沒有辦法進(jìn)行控制。因為我們更本就沒有辦法知道CPU什么時候來執(zhí)行他們,執(zhí)行他們的次序又是什么?
有人要問沒有辦法控制那我們?nèi)绾问呛媚兀窟@個問題也正是我這里要向大家進(jìn)行解釋和說明的,雖然我們不能夠控制他們的運行,但我們可以做一些手腳來達(dá)到我們自己的意志。
這里我們的做手腳也就是對線程進(jìn)行同步,關(guān)于同步的概念大家在《操作系統(tǒng)》中應(yīng)該都看過吧!不了解的話,我簡單說說:讀和寫的關(guān)系(我讀書的時候,請你不要在書上亂寫,否則我就沒有辦法繼續(xù)閱讀了。)
處理有兩種:用戶方式和內(nèi)核方式。
用戶方式的線程同步由于有好幾種:原子訪問,關(guān)鍵代碼段等。
在這里主要向大家介紹關(guān)鍵代碼段的處理(我個人用的比較多,簡單實用)。先介紹一下它的一些函數(shù),隨后提供關(guān)鍵代碼段的處理類供大家參考(比較小,我就直接貼上來了)
VOID InitializeCriticalSection( //初始化互斥體
LPCRITICAL_SECTION lpCriticalSection // critical section
);
VOID DeleteCriticalSection( //清除互斥體
LPCRITICAL_SECTION lpCriticalSection // critical section
);
VOID EnterCriticalSection( //進(jìn)入等待
LPCRITICAL_SECTION lpCriticalSection // critical section
);
VOID LeaveCriticalSection( //釋放離開
LPCRITICAL_SECTION lpCriticalSection // critical section
);
以上就是關(guān)于關(guān)鍵代碼段的基本API了。介紹就不必了(MSDN)。而我的處理類只是將這幾個函數(shù)進(jìn)行了組織,也就是讓大家能夠更加理解關(guān)鍵代碼端
.h
class CCriticalSection //共享變量區(qū)類
{
public:
CCriticalSection();
virtual ~CCriticalSection();
void Enter(); //進(jìn)入互斥體
void Leave(); //離開互斥體釋放資源
private:
CRITICAL_SECTION g_CritSect;
};
.cpp
CCriticalSection::CCriticalSection()
{
InitializeCriticalSection(&g_CritSect);
}
CCriticalSection::~CCriticalSection()
{
DeleteCriticalSection(&g_CritSect);
}
void CCriticalSection::Enter()
{
EnterCriticalSection(&g_CritSect);
}
void CCriticalSection::Leave()
{
LeaveCriticalSection(&g_CritSect);
}
由于篇幅有限關(guān)鍵代碼段就說到這里,接下來向大家簡單介紹一下內(nèi)核方式下的同步處理。
哎呀!這下可就慘了,這可是要說好多的哦!書上的羅羅嗦嗦我就不說了,我就說一些我平時的運用吧。首先內(nèi)核對象和一般的我們使用的對象是不一樣的,這樣的一些對象我們可以簡單理解為特殊對象。而我們內(nèi)核方式的同步就是利用這樣的一些特殊對象進(jìn)行處理我們的同步,其中包括:事件對象,互斥對象,信號量等。對于這些內(nèi)核對象我只向大家說明兩點:
1.內(nèi)核對象的創(chuàng)建和銷毀
2.內(nèi)核對象的等待處理和等待副作用
第一:內(nèi)核對象的創(chuàng)建方式基本上而言都沒有什么太大的差別,例如:創(chuàng)建事件就用HANDLE CreateEvent(…..),創(chuàng)建互斥對象 HANDLE CreateMutex(…….)。而大家注意的也是這三個內(nèi)核對象在創(chuàng)建的過程中是有一定的差異的。對于事件對象我們必須明確指明對象是人工對象還是自動對象,而這種對象的等待處理方式是完全不同的。什么不同下面說(呵呵)。互斥對象比較簡單沒什么說的,信號量我們創(chuàng)建必須注意我們要定義的最大使用數(shù)量和初始化量。最大數(shù)量>初始化量。再有如果我們?yōu)槲覀兊膬?nèi)核對象起名字,我們就可以在整個進(jìn)程中共用,也可以被其他進(jìn)程使用,只需要OPEN就可以了。也就不多說了。
第二:內(nèi)核對象的等待一般情況下我們使用兩個API:
DWORD WaitForSingleObject( //單個內(nèi)核對象的等待
HANDLE hHandle, // handle to object
DWORD dwMilliseconds // time-out interval
);
DWORD WaitForMultipleObjects( //多個內(nèi)核對象的等待
DWORD nCount, // number of handles in array
CONST HANDLE *lpHandles, // object-handle array
BOOL fWaitAll, // wait option
DWORD dwMilliseconds // time-out interval
);
具體怎么用查MSDN了。
具體我們來說等待副作用,主要說事件對象。首先事件對象是分兩種的:人工的,自動的。人工的等待是沒有什么副作用的(也就是說等待成功后,要和其他的對象一樣要進(jìn)行手動釋放)。而自動的就不一樣,但激發(fā)事件后,返回后自動設(shè)置為未激發(fā)狀態(tài)。這樣造成的等待結(jié)果也不一樣,如果有多個線程在進(jìn)行等待事件的話,如果是人工事件,被激活后所有等待線程成執(zhí)行狀態(tài),而自動事件只能有其中一個線程可以返回繼續(xù)執(zhí)行。所以說在使用這些內(nèi)核對象的時候,要充分分析我們的使用目的,再來設(shè)定我們創(chuàng)建時候的初始化。簡單的同步我就說到這里了。下面我就將將我們一般情況下處理游戲服務(wù)器處理過程中的數(shù)據(jù)保護(hù)問題分析:
首先向大家說說服務(wù)器方面的數(shù)據(jù)保護(hù)的重要性,圖例如下:
用戶列表
用戶刪除
用戶數(shù)據(jù)修改
使用數(shù)據(jù)
加入隊列
對于上面的圖例大家應(yīng)該也能夠看出在我們的游戲服務(wù)器之中,我們要對于我們用戶的操作是多么的頻繁。如此頻繁的操作我們?nèi)绻贿M(jìn)行處理的話,后果將是悲慘和可怕的,舉例:如果我們在一個線程刪除用戶的一瞬間,有線程在使用,那么我們的錯誤將是不可難以預(yù)料的。我們將用到了錯誤的數(shù)據(jù),可能會導(dǎo)致服務(wù)器崩潰。再者我們多個線程在修改用戶數(shù)據(jù)我們用戶數(shù)據(jù)將是沒有辦法保持正確性的。等等情況都可能發(fā)生。怎么樣杜絕這樣的一些情況的發(fā)生呢?我們就必須要進(jìn)行服務(wù)器數(shù)據(jù)的保護(hù)。而我們?nèi)绾握_的保護(hù)好數(shù)據(jù),才能夠保持服務(wù)器的穩(wěn)定運行呢?下面說一下一些實際處理中的一些經(jīng)驗之談。
1.我們必須充分的判斷和估計我們服務(wù)器中有那些數(shù)據(jù)要進(jìn)行數(shù)據(jù)保護(hù),這些就需要設(shè)計者和規(guī)劃者要根據(jù)自己的經(jīng)驗進(jìn)行合理的分析。例如:在線用戶信息列表,在線用戶數(shù)據(jù)信息,消息列表等。。。。。
2.正確和十分小心的保護(hù)數(shù)據(jù)和正確的分析要保護(hù)的數(shù)據(jù)。大家知道我們要在很多地方實現(xiàn)我們的保護(hù)措施,也就是說我們必須非常小心謹(jǐn)慎的來書寫我們的保護(hù),不正確的保護(hù)會造成系統(tǒng)死鎖,服務(wù)器將無法進(jìn)行下去(我在處理的過程中就曾經(jīng)遇到過,頭都大了)。正確的分析要保護(hù)的數(shù)據(jù),也就是說,我們必須要估計到我們要保護(hù)的部分的處理能夠比較快的結(jié)束。否則我們必須要想辦法解決這個問題:例如:
DATA_STRUCT g_data;
CRITICAL_SECTION g_cs;
EnterCriticalSection(&g_cs);
SendMessage(hWnd,WM_ONEMSG,&g_data,0);
LeaveCriticalSection(&g_cs);
以上處理就有問題了,因為我們不知道SendMessage()什么時候完成,可能是1/1000豪秒,也可能是1000年,那我們其他的線程也就不用活了。所以我們必須改正這種情況。
DATA_STRUCT g_data;
CRITICAL_SECTION g_cs;
EnterCriticalSection(&g_cs);
PostMessage(hWnd,WM_ONEMSG,&g_data,0);
LeaveCriticalSection(&g_cs);
或者 DATA_STRUCT temp_data;
EnterCriticalSection(&g_cs);
temp_data = g_cs;
LeaveCriticalSection(&g_cs);
SendMessage(hWnd,WM_ONEMSG,& temp_data,0);
3.最好不要復(fù)合保護(hù)用戶數(shù)據(jù),這樣可能會出現(xiàn)一些潛在的死鎖。
簡而言之,服務(wù)器的用戶數(shù)據(jù)是一定需要進(jìn)行保護(hù),但我們在保護(hù)的過程中就一定需要萬分的小心和謹(jǐn)慎。這篇我就說到這里了,具體的還是需要從實踐中來進(jìn)行學(xué)習(xí),下節(jié)想和大家講講服務(wù)器的場景處理部分。先做事去了。呵呵!!有好的想法和建議的和我交流探討,先謝謝了。
只有注冊用戶登錄后才能發(fā)表評論。 | ||
![]() |
||
網(wǎng)站導(dǎo)航:
博客園
IT新聞
Chat2DB
C++博客
博問
|
||
相關(guān)文章:
|
||