2010年8月16日 星期一

Multi Thread 常用的安全控制

Thread 有至少 3 種狀態供我們利用,分別為
1. Running - 正在佔用CPU執行中
2. Ready - 可立即執行的狀態,但CPU仍被佔用
3. Waiting - 等待事件

撰寫多緒的程式常常會發生資料先後被讀取、修改的狀況,所以必須適時的加入鎖定機制來控制資料的安全性,常用的方法有三種…

1. Binary Semaphore
以 BOOL 值標示佔用或非佔用,若有 Thread 在使用某些資料或做某些事即將旗標設為 1,新的Thread在旗標未被標示為0之前會被設為等待狀態,有另一種方式可以實作多個 Thread 共用資料的方式,稱為 Semaphore,將已知可使用資料的Thread數量設為UINT unSemaphore的初始值,有 Thread 使用時即將 -- 旗號,至有新的 Thread 碰到旗號為小於 0 時即等待,舊的Thread執行完畢即 ++ 旗號,從等待中的 Thread Queue 第一個設為 Ready 狀態。

2. Mutex
與 Binary Semaphore 相似,但前者的旗標可以被系統中任意的 Thread 讀取並改變狀態,Mutex 也可被任一 Thread 讀取,但只許可獲得 Mutex 的 Thread 親自釋放 Mutex,其餘 Thread 是無法改變狀態的。

3. Critical Section
比 Mutex 更加嚴謹,Mutex 可以被任一 Thread 讀取,Critical Section 只能被鎖定它的 Thread 讀取並釋放。

在MFC中常用的類別為 CSemaphore、CMutex、CCriticalSection 與 CSingleLock 搭配使用,例如


CCirticalSection m_csGetFileMD5;
CSingleLock sFileMD5Lock(&m_csGetFileMd5);
sFileMD5Lock.Lock();
BeginThread(某個Thread);
sFileMD5Lock.UnLock();


另外實務上不只在資料同步與安全性的問題需要用到鎖定,繁複的操作也是常用到的,舉個例子,例如使用 Browser 時,在非常短的時間內瘋狂點擊頁面上的 URL,每個點擊 URL 即會做導入該連結的動作,若不處理這樣的行為會發生奇怪的事,例如點擊了 A、B、C、D、E 五個 URL,過了幾秒後完成了 A 的請求頁面就跳至 A 嘍。

解決方法就是在給予每個 Click 動作所啟動的 Thread 參數中加入一個 RequestID 或記下最後一個點選的 URL 唯一識別資訊、例如完整的 URL 即可在 OpenUrlThread(PVOID *param) 中做這樣的處理


status URLData
{
  UINT m_unRequestID;
  // RequestID 這個值存入當時的 CUP Tick Time 是個不錯的選擇
  // 在 Win32 底下就是使用::GetTickCount();來獲得 Tick Time
  TCHAR *m_tcURL;
};

UINT OpenUrlThread(PVOID *param)
{
  URLData *urlData = (URLData *)param;
  if(m_nRequestID <> urlData->m_unRequestID)
  {
    return;
  }
  // 處理 URL 的正事中若有 loop 等程式碼,也在條件中加入 RequestID 的判斷,
  // return 完成動作時也加入判斷,即可在最短時間結束已過期的行為,
  // 並可防止送出非最終請求的結果。
  if(m_nRequestID <> urlData->m_unRequestID)
  {
    GetParent()->PostMessage(處理好了,做下一件事吧);
  }
  return TRUE;
}

沒有留言:

張貼留言