天天看點

多線程程式操作共享區域(檔案)的一點體會

     最近比較忙,很久沒有寫部落格了,持續長時間的程式設計,使得我完全淪為程式匠人。但是感覺卻不是想别人那麼糟糕,畢業已經快兩年了,我為我的程式設計興趣仍然如此強烈而感到欣慰,也對一直以來比較關心的“行業應用軟體架構設計”有了更深的了解,這堅定了我的信念!

     今天晚上,終于有了一點點閑暇的時間,就想大家分享下“多線程程式操作共享區域(檔案)”的一些體會吧!

     多線程相信大家都陌生吧,多線程程式操作共享區域應該也不陌生吧,但是大家是否經曆過多CPU的伺服器下同時100個線程,操作離散的檔案呢?如果每個線程隻負責一個固定的檔案,那麼問題也就不是問題了,但是如果離散的檔案,有可能同時被多線程都讀寫的,那麼是否能保證檔案讀寫的資料一緻性、是否能保證資料在存儲的時候由于非次序存儲而導緻資料丢失呢?另外,又怎麼解決同一段時間同一個線程操作同一個檔案的效率問題呢,如果有方法解決,有怎麼保證不出現上述第一個問題呢?

     擺在面前的二個問題,其實都是編寫程式時候,尤其是性能要求比較高的時候,特别需要注意的問題。如下将一一介紹我的一些體會;

如何解決多線程多檔案操作的資料一緻和丢失問題

   解決這個問題,我們要先思考一下,究竟是什麼原因導緻這個問題呢?答案很明顯,主要因為多線程多檔案存儲時候的時候頻繁打開和頻繁關閉無次序性導緻的,這有點想資料庫一樣,DBMS是怎麼來解決這個問題呢?DBMS有兩個方法,一是事務、二是雙端鎖,其實這兩個方法是一個樣的道理,在這裡我們就不做介紹,有興趣大家可以查尋些其他的資料。這裡我介紹實際應用中我的方法。

    我的方法是,保證一個檔案在操作的時候,隻打開一個執行個體,打開之後隻有一次關閉。

    首先來看看,我是如何來設計這樣的檔案處理單元,我們起名為CDataHandlerUint,大家當作其為結構體就可以了,如下代碼;

    typedef struct _DATA_HANDLER_UINT{queue<bool> m_OpenStatusQ; TiXmlDocument m_doc;CString m_strXmlPath;DWORD m_dwOldTicks;}CDataHandlerUint,*PCDataHandlerUint;

    由于我們操作XML檔案,是以在這個結構體中,我們儲存了一個打開的XML檔案執行個體——m_doc。另外一個重要的成員是m_OpenStatusQ這個暫時命名為XML檔案打開請求隊列,用于記錄檔案讀寫次數。其他成員随後會逐一介紹。

    如下來看看,這個結構體線上程内部有是怎麼用的,我們還需要一個關鍵的管理這個結構體的成員,CMap<CString,LPCSTR,PCDataHandlerUint,PCDataHandlerUint> m_mapHandlerUint,m_mapHandlerUint就用來管理這個結構體的。其中m_strXmlPath就是這個MAP的KEY,用來直接搜尋出CDataHandlerUint的資料處理單元的pointer。

    那麼線上程中又怎麼來應用這樣的一個成員呢?看下如下這個線程的處理過程就明白了;

    UINT32 DataHandlerThread(LPVOID pThis){

          (1) 擷取要操作的檔案路徑

          (2) 通過檔案路徑,擷取儲存在m_mapHandlerUint的Pointer.

          (3) 如果這個Pointer為NULL,重新NEW,調用m_OpenStatusQ.push(true),之後并添加到MAP中。

         (4)如果這個Pointer不為NULL,調用m_OpenStatusQ.push(true)。

          ............

          (5) 調用m_OpenStatusQ.pop(),之後檢測m_OpenStatusQ的size ,如果size為0,儲存并關閉XML檔案,否則,在根據m_dwOldTicks判斷是否逾時,如果逾時同樣儲存并關閉XML檔案,否則結束函數

    }

     這樣就應用了MAP加上queue順利的解決了這個問題,大家看了之後,仔細想想吧!

如果線程操作檔案時,如果檔案存在一定順序,那麼怎麼提高效率呢?

     如果一個線程式遇到一個這樣的檔案系列怎麼辦,如下檔案系列;

      A.xml

      B.xml

      C.xml

      ......

     從這裡可以看出,檔案是有順序的,那麼如何提高效率呢?

     答案就是,盡量減少檔案打開和關閉次數,重複利用已經打開的檔案句柄!

     這裡程式設計的方法其實很簡單,隻需擷取上一次操作檔案句柄,并在上次操作的時候不關閉檔案,關閉檔案的操作放在調用之外,這樣當發現兩個句柄一樣的時候,即調用儲存并關閉,當然這裡考慮到第一個問題,是以儲存并關閉的條件仍然是調用m_OpenStatusQ.pop(),之後檢測m_OpenStatusQ的size ,如果size為0,儲存并關閉XML檔案,否則,在根據m_dwOldTicks判斷是否逾時,如果逾時同樣儲存并關閉XML檔案,否則結束函數。

    上述的兩個問題,兩個方法,隻介于本人的體會,肯定還有更好的方法,如大家有興趣可以聯系我本人,友善更深一步的探讨。