Windows 下的線程同步

來源:互聯網
上載者:User

這段時間一直在做Windows下的項目開發, 最近花了點時間翻了翻書, 總結一下Windows常用的線程同步方法.

 


一、線程同步--SendMessage()

1. SendMessage()是一種簡單方便的視窗和工作者線程間的資料同步的方法
2. 當工作者線程將資料準備好後,可以通過調用SendMessage()將訊息發送給指定的視窗,這時發送線程將被阻塞直到SendMessage()返回.

LRESULT SendMessage(
    HWND hWnd, // 目標視窗控制代碼
    UINT Msg, // 被發送的訊息
    WPARAM wParam, // 第一個訊息參數
    LPARAM lParam // 第二個訊息參數
);

注意: 類似功能的PostMessage是不阻塞的, 將訊息發送到UI主線程訊息佇列直接返回.

 

二、線程同步--臨界地區(Critical Section)

1. 臨界區一次只允許一個線程取得對某個資料區的訪問權,保證同一時刻只能有一個線程進入臨界區(獲得臨界區對象)。
2. 臨界區非常適合對一個進程中的資料的訪問,因為它們的速度很快.
3. 臨界區對象不是核心對象,它不由作業系統的低級組件管理,而且不能使用控制代碼來操縱。
4. 在進程中建立一個臨界區,即在進程中分配一個CRITICAL_SECTION資料結構,該臨界區結構的分配必須是全域的,這樣該進程的不同線程就能訪問它.
5. 在使用臨界區同步線程之前,必須調用InitializeCriticalSection來初始化臨界區,釋放資源之前,只需要初始化一次。
6. 調用VOID EnterCriticalSection()來進入臨界區,如果臨界區對象被別的線程佔用,該線程阻塞進入睡眠,直到佔有臨界區對象的線程釋放臨界區對象,且在被喚醒之前,系統不會給它分配CPU。
7. 調用VOID TryEnterCriticalSection()來進入臨界區,如果臨界區對象被別的線程佔用,返回FALSE,線程不進入睡眠狀態,繼續執行.
8. 調用LeaveCriticalSection()離開臨界區,當擁有臨界區所有權的線程調用LeaveCriticalSection放棄所有權時,系統只喚醒正等待中的一個線程,給它所有權,其它線程則繼續睡眠。
9. 注意, EnterCriticalSection() 和 LeaveCriticalSection()務必保證成對調用, 當一個佔有臨界區的線程多次調用EnterCriticalSection(), 將使得臨界區對象的引用計數增加一, 如果想要徹底放棄臨界區擁有權,必須調用同樣多的LeaveCriticalSection()
10. 調用DeleteCriticalSection釋放臨界區資源.

typedef struct _RTL_CRITICAL_SECTION
{
long LockCount;
long RecursionCount;
HANDLE oWningThread; //from the thread's ClientId->UniqueThread
HANDLE LockSemaphore;
DWORD SpinCount;

}PTL_CRITICAL_SECTION,*PRTL_CRITICAL_SECTION;

typedef PRTL_CRITICAL_SECTION LPCRITICAL_SECTION
typedef RTL_CRITICAL_SECTION CRITICAL_SECTION

VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
VOID EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
BOOL TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
VOID DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection)

 

 

範例程式碼:

TryEnterCriticalSection(&critical_section)
TODO: 這裡對同步資料進行操作
LeaveCriticalSection(&critical_section)

if (TryEnterCriticalSection(&critical_section))
{
    TODO: 這裡對同步資料進行操作
    LeaveCriticalSection(&critical_section);
}


三、線程同步--核心對象

1. 適用多個進程的線程間資料同步.
2. 同樣也能實現單個進程中的線程同步,但是效率要比使用臨界區方式要差些。
4. 下列核心對象可用來同步線程:
    進程,Processes
    線程,Threads
    檔案,Files
    控制台輸入,Console input
    檔案變化通知,File change notifications
    互斥量,Mutexes
    訊號量,Semaphores
    事件(自動重設事件和手動重設事件),Events
    可等的計時器(只用於Window NT4或更高),Waitable timers
    Jobs
5. 在所有的核心對象中,事件核心對象是個最基本的對象
6. 使用CreateEvent()函數建立事件核心對象
7. 使用OpenEvent()函數開啟事件核心對象
8. CloseHandle() 來釋放核心對象
9. 使用SetEvent()將事件改為已通知狀態:
10. 使用ResetEvent()函數時,可以將該事件改為未通知狀態:
11. 在工作者現程中使用WaitForSingleObject()函數用來檢測hHandle事件的訊號狀態

函數功能描述:建立或開啟一個命名的或無名的事件對象

函數原型:

HANDLE CreateEvent(
  LPSECURITY_ATTRIBUTES lpEventAttributes,      // 安全屬性
  BOOL             bManualReset,       // 複位方式
  BOOL             bInitialState,       // 初始狀態
  LPCTSTR         lpName           // 對象名稱
);

DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds);
參數hHandle是一個事件的控制代碼,第二個參數dwMilliseconds是時間間隔。如果時間是有訊號狀態返回WAIT_OBJECT_0,如果時間超過dwMilliseconds值但時間事件還是無訊號狀態則返回WAIT_TIMEOUT
但如果參數dwMilliseconds為INFINITE時函數將直到相應時間事件變成有訊號狀態才返回,否則就一直等待下去,直到WaitForSingleObject有返回直才執行後面的代碼

範例程式碼:

人工重設的事件使用: 主控線程當設定SetEvent後, 背景工作執行緒A和背景工作執行緒B將同時開始工作,比如可以同時對一段資料進行並行讀操作.

主控線程:
     HANDLE hEvent; // 全域

     hEvent = CreateEvent (NULL, TRUE, FALSE, NULL) ; // 建立事件對象, 告訴系統是建立一個人工重設事件對象
    
     SetEvent (hEvent) ; // 控制背景工作執行緒MyThreadA/MyThreadB開始工作

背景工作執行緒A和B:
     UINT MyThreadA( LPVOID pParam )
     {
         for(;;)
        {
             WaitForSingleObject(hEvent,INFINITE);
            // 背景工作執行緒做一些事情
        }

         return 0;
     }

     UINT MyThreadB( LPVOID pParam )
     {
         for(;;)
        {
             WaitForSingleObject(hEvent,INFINITE);
            // 背景工作執行緒做一些事情
        }

         return 0;
     }

自動重設的事件使用: 主控線程當設定SetEvent後, 背景工作執行緒A和背景工作執行緒B將有一個開始工作,而另一個將繼續等待狀態, 直到一個操作完成另一個背景工作執行緒才開始工作. 比如多個線程對一段資料進行寫操作,需要把它們序列化

主控線程:
     HANDLE hEvent; // 全域

     hEvent = CreateEvent (NULL, FALSE, FALSE, NULL) ; // 建立事件對象, 告訴系統是建立一個自動重設事件對象
    
     SetEvent (hEvent) ; // 控制背景工作執行緒MyThreadA/MyThreadB開始工作

背景工作執行緒A和B:
     UINT MyThreadA( LPVOID pParam )
     {
         for(;;)
        {
             WaitForSingleObject(hEvent,INFINITE);
            // 這裡做一些事情

        SetEvent (hEvent);
        }

         return 0;
     }

     UINT MyThreadB( LPVOID pParam )
     {
         for(;;)
        {
             WaitForSingleObject(hEvent,INFINITE);
            // 這裡做一些事情

        SetEvent (hEvent);
        }

         return 0;
     }

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.