程式設計思想之多執行緒與多程式(4):C++ 中的多執行緒

發表於2015-10-26

程式設計思想之多執行緒與多程式(1)——以作業系統的角度述說執行緒與程式》一文詳細講述了執行緒、程式的關係及在作業系統中的表現,《程式設計思想之多執行緒與多程式(2)——執行緒優先順序與執行緒安全》一文講了執行緒安全(各種同步鎖)和優先順序,這是多執行緒學習必須瞭解的基礎。本文將接著講一下C++中多執行緒程式的開發.這裡主要講Windows平臺執行緒的用法,建立執行緒要呼叫windows API的CreateThread方法。

 

建立執行緒

在Windows平臺,Windows API提供了對多執行緒的支援。前面程式和執行緒的概念中我們提到,一個程式至少有一個執行緒,這個執行緒稱為主執行緒(main thread),如果我們不顯示地建立執行緒,那我們產的程式就是隻有主執行緒的間執行緒程式。
下面,我們看看Windows中執行緒相關的操作和方法:

CreateThread與CloseHandle

CreateThread用於建立一個執行緒,其函式原型如下:

**說明:**lpThreadAttributes:指向SECURITY_ATTRIBUTES結構的指標,決定返回的控制程式碼是否可被子程式繼承,如果為NULL則表示返回的控制程式碼不能被子程式繼承。

dwStackSize :執行緒棧的初始化大小,位元組單位。系統分配這個值對

lpStartAddress:指向一個函式指標,該函式將被執行緒呼叫執行。因此該函式也被稱為執行緒函式(ThreadProc),是執行緒執行的起始地址,執行緒函式是一個回撥函式,由作業系統線上程中呼叫。執行緒函式的原型如下:

lpParameter:傳入執行緒函式(ThreadProc)的引數,不需傳遞引數時為NULL

dwCreationFlags:控制執行緒建立的標誌,有三個型別,0:執行緒建立後立即執行執行緒;CREATE_SUSPENDED:執行緒建立後進入就緒狀態,直到執行緒被喚醒時才呼叫;STACK_SIZE_PARAM_IS_A_RESERVATION:dwStackSize 引數指定執行緒初始化棧的大小,如果STACK_SIZE_PARAM_IS_A_RESERVATION標誌未指定,dwStackSize將會設為系統預留的值。

返回值:如果執行緒建立成功,則返回這個新執行緒的控制程式碼,否則返回NULL。如果執行緒建立失敗,可通過GetLastError函式獲得錯誤資訊。

可用這個函式關閉建立的執行緒控制程式碼,如果函式執行成功則返回true(非0),如果失敗則返回false(0),如果執行失敗可呼叫GetLastError.函式獲得錯誤資訊。

【Demo1】:建立一個最簡單的執行緒

結果如下:

【Demo2】:線上程函式中傳入引數

結果:

CreateMutex、WaitForSingleObject、ReleaseMutex

從【Demo2】中可以看出,雖然建立的子執行緒都正常執行起來了,但輸出的結果並不是我們預期的效果。我們預期的效果是每輸出一條語句後自動換行,但結果卻並非都是這樣。這是因為線上程執行時沒有做同步處理,比如第一行的輸出,主執行緒輸出“主執行緒 ===”後時間片已用完,這時輪到子執行緒1輸出,在子執行緒輸出“執行緒1 —”後時間片也用完了,這時又輪到主執行緒執行輸出“0”,之後又輪到子執行緒1輸出“0”。於是就出現了“主執行緒 === 執行緒1 — 0 0”的結果。

主執行緒:cout << “主執行緒 === ” << i << endl;
子執行緒:cout << pThreadData->strThreadName << ” — ” << i << endl;

為避免出現這種情況,我們對執行緒做一些簡單的同步處理,這裡我們用互斥量(Mutex),關於互斥量(Mutex)的概念,請看《程式設計思想之多執行緒與多程式(2)——執行緒優先順序與執行緒安全》一文;更多C++執行緒同步的處理,請看下一節。

在使用互斥量進行執行緒同步時會用到以下幾個函式:

**說明:**lpMutexAttributes也是表示安全的結構,與CreateThread中的lpThreadAttributes功能相同,表示決定返回的控制程式碼是否可被子程式繼承,如果為NULL則表示返回的控制程式碼不能被子程式繼承。bInitialOwner表示建立Mutex時的當前執行緒是否擁有Mutex的所有權,若為TRUE則指定為當前的建立執行緒為Mutex物件的所有者,其它執行緒訪問需要先ReleaseMutex。lpName為Mutex的名稱。

**說明:**WaitForSingleObject的作用是等待一個指定的物件(如Mutex物件),直到該物件處於非佔用的狀態(如Mutex物件被釋放)或超出設定的時間間隔。除此之外,還有一個與它類似的函式WaitForMultipleObjects,它的作用是等待一個或所有指定的物件,直到所有的物件處於非佔用的狀態,或超出設定的時間間隔。

hHandle:要等待的指定物件的控制程式碼。dwMilliseconds:超時的間隔,以毫秒為單位;如果dwMilliseconds為非0,則等待直到dwMilliseconds時間間隔用完或物件變為非佔用的狀態,如果dwMilliseconds 為INFINITE則表示無限等待,直到等待的物件處於非佔用的狀態。

說明:釋放所擁有的互斥量鎖物件,hMutex為釋放的互斥量的控制程式碼。

【Demo3】:執行緒同步

結果:

為進一步理解執行緒同步的重要性和互斥量的使用方法,我們再來看一個例子。

買火車票是大家春節回家最為關注的事情,我們就簡單模擬一下火車票的售票系統(為使程式簡單,我們就抽出最簡單的模型進行模擬):有500張從北京到贛州的火車票,在8個視窗同時出售,保證系統的穩定性和資料的原子性。

【Demo4】:模擬火車售票系統

SaleTickets.cpp :

測試程式:

結果:

相關文章