摘 要:針對實時數據監(jiān)控系統(tǒng)普遍存在的數據同步問題提出改進方法,利用Windows內核對象的特性編程設計了共享鎖類,并與未使用鎖、使用互斥鎖兩種情況進行時序比較,結果表明共享鎖在同步數據和優(yōu)先保證寫者效率方面具有明顯的優(yōu)越性。這些特性使共享鎖在工業(yè)控制級別的實時軟件系統(tǒng)中具有重要的現實意義。
關鍵詞:數據同步共享鎖實時數據監(jiān)控
中圖分類號:TM73文獻標識碼:A文章編號:1674-098X(2011)05(c)-0018-01
1 引言
在工控組態(tài)軟件領域,普遍存在一個稱作讀者寫者的問題,即對某些資源的訪問,存在兩種可能的情況,一種訪問必須是排他的,稱作寫操作;另一種訪問可以是共享的,稱為讀操作。對于一個實時數據監(jiān)控系統(tǒng)來說,由于Windows是一個多任務搶占式的操作系統(tǒng),在多個線程共同訪問一個數據區(qū)的環(huán)境下,會產生數據同步的問題。比如:有提供數據的服務程序A和若干界面顯示程序B、C、D等。服務程序和顯示程序共同使用一塊數據內存區(qū)E,其中服務程序負責更新內存數據(寫操作),而顯示程序則定時訪問內存區(qū)數據(讀操作)。假設數據內存區(qū)E中變量的數據結構為:
FILETIMEftTime;//時間
DWORD dwValue;//值
那么,在B剛讀取完某個變量的ftTime值后,插入了A的寫操作,這時候再讀取到的dwValue值就和ftTime不一致了。這種不一致的現象必須通過數據同步來避免。所以,對內存區(qū)E的讀寫訪問必須作以下兩個約束:
1)A正在進行寫操作時,B、C、D必須等待該操作完成才能訪問E;約束條件(1)
2)B、C、D正在訪問E時,A 必須等待該操作完成才對E進行寫操作;約束條件(2)
以上約束可以簡單的歸納為:讀寫互斥。
2 一般解決方法
在Windows下,系統(tǒng)提供了事件、互斥量、信號量等核心對象以及一些等待函數來進行多線程同步。單獨使用這些核心對象均能夠實現讀寫的互斥。
但是單純的實現讀寫互斥并不能完全滿足實時監(jiān)控系統(tǒng)對訪問效率的要求。因為讀操作的并發(fā)并不會引起數據的不同步,為了提高效率,必須在之前的兩個約束的基礎上增加讀寫訪問的另一約束,即約束(3):B、C、D能夠同時訪問E。
3 共享鎖
3.1 數據結構
利用Windows內核對象的特性,可以編程實現滿足上述三點約束條件的共享鎖類。
共享鎖的內部數據結構由讀事件、寫事件、互斥對象和讀者計數組成。
其中,讀、寫事件是一對命名的、手動重置的事件,初始化為有信號狀態(tài)。事件可以使用Windows Api函數CreateEvent、ResetEvent和SetEvent 來創(chuàng)建、重置和置位。這對事件成員的作用是實現讀寫的互斥。
共享鎖中的互斥對象是一個初始化無所有者的命名互斥量。互斥量可以使用CreateMutex、WaitForSingleObject和ReleaseMutex來創(chuàng)建、進入和離開?;コ鈱ο笤诠蚕礞i中的作用有兩個:對寫操作進行保護,保證一個時刻內只有一個線程在進行寫操作;在多個讀線程中保護讀者計數,保證所有讀線程中對讀者計數的操作都是原子操作。
讀者計數實際上是一片命名的內存區(qū),可以用CreateFileMapping和MapViewOfFile來創(chuàng)建和映射該內存區(qū)。讀者計數用于對同一時刻內的并發(fā)讀操作進行管理。
3.2 讀寫操作流程
3.2.1 寫操作
數據服務A在對數據內存區(qū)E進行寫數據操作時,首先重置寫事件。這時候寫事件為無信號狀態(tài),這就意味著B、C、D等讀線程將在“等待 寫”這一步中掛起,不再進行讀操作。接下來A將等待讀事件,如果之前時刻已經有若干線程在進行讀操作,那么A將等到這些操作完成后進入互斥對象。這就意味著A已經獨占了內存區(qū)E這塊資源,可以放心地進行寫操作了。
在A完成寫操作后,首先離開互斥對象,釋放之前獨享的這塊資源,再置位寫事件,讓B、C、D等讀線程結束掛起狀態(tài),進行讀操作。
3.2.2 讀操作
假設B線程某一時刻想以讀者的身份訪問數據區(qū),B首先等待寫事件,如果寫操作正在進行,那么B將被掛起直到寫操作完成。接下來B將訪問讀者計數,如果讀者計數為0,B將重置讀事件;否則意味著在前一時刻已經有另外的讀者(C或者D)已經重置了讀事件,B將讀者計數自增后直接進入讀操作。當然,在訪問讀者計數的整個過程中都使用互斥對象對計數進行保護。
在完成讀操作后,B再次訪問讀者計數字,如果發(fā)現此時B已經是最后一位讀者(計數=1),B將置位讀事件,計數自減后退出。
4 比較和結果
4.1 實驗平臺的搭建
創(chuàng)建一個寫者線程,三個讀者線程,同時運行。在每個線程內,記錄下每次操作的性質(讀或寫)、操作時間和操作者(線程),保存到日記文件中,是為原始數據。分三次進行實驗:1無數據同步;2使用互斥對象進行數據同步;3使用文中所述共享鎖進行數據同步。
4.2 比較和結果
未使用鎖情況下,由于Windows是搶占式的操作系統(tǒng),A、B、C、D四個進程將對E資源進行無序的競爭。
使用了互斥鎖情況下,在B、C、D的讀周期內不可能再出現值被A修改的情況。但是這樣的數據同步的缺陷也是明顯的:在B的讀周期內A、C和D都在掛起等待狀態(tài),消耗了不必要的時間;另外,在這樣的數據同步模式下,讀者和寫者的地位是平等的,A作為數據的提供者卻必須要和B、C、D同等的去搶占資源,而且讀者越多負擔越重,是一種很不合理的作法。同樣,在B、C、D的讀周期內數據不會被A改動,解決了數據同步的問題。另外,B、C、D可以并發(fā)進行,因為多個讀者訪問不會對資源E造成破壞,避免了A不必要的等待時間,提高了效率。
更重要的是,共享鎖對讀者和寫者的權限進行了區(qū)分:寫者優(yōu)先于讀者訪問共享資源。這是因為任意時刻下A想對資源E進行寫操作時都會重置寫事件,這時試圖進行讀操作的其他讀者就被迫進入等待狀態(tài),A只需要等待前一時刻已經進入讀周期的進程完成操作。這就保證了服務程序A的工作效率,也就是保證數據的實時性。
5 結語
綜上所述,共享鎖具有同步數據和優(yōu)先保證寫者效率的工作特性。這些獨特的工作使得這樣的共享鎖在工業(yè)控制級別的實時軟件系統(tǒng)中具有很重要的現實意義。在實時數據庫、實時監(jiān)控系統(tǒng)、數據報警系統(tǒng)等領域的軟件開發(fā)中具有廣泛的應用前景。
參考文獻
[1]Jeffrey Richter. Windows核心編程[M].北京:機械工業(yè)出版社,2008,5.