目錄
- 緒論
- 臨界區 CCriticalSection
-
- CCriticalSection.demo
- 事件 CEvent
-
- CEvent.demo
- 互斥量 CMutex
-
- CMutex.demo
- 信号量 CSemaphore
-
- CSemaphore.demo
- 參考資料
緒論
線程同步有四種方式:
- 臨界區(CCriticalSection)
- 事件(CEvent)
- 互斥量(CMutex)
- 信号量(CSemaphore)
臨界區 CCriticalSection
将同步變量劃分到一個區域内。在一個線程通路時,調用CCriticalSection的成員函數lock(),此時如果有其他線層通路,則此線程會被挂起,并且放入一個系統隊列中等待。知道目前線程調用Unlock()釋放臨界區。
CCriticalSection.demo
- 建立一個基于對話框的工程MultiThread8,在對話框IDD_MULTITHREAD8_DIALOG中加入兩個按鈕和兩個編輯框控件,兩個按鈕的ID分别為IDC_WRITEW和IDC_WRITED,标題分别為“寫‘W’”和“寫‘D’”;兩個編輯框的ID分别為IDC_W和 IDC_D,屬性都選中Read-only;
- 在MultiThread8Dlg.h檔案中聲明兩個線程函數: UINT WriteW(LPVOID pParam);UINT WriteD(LPVOID pParam); 使用ClassWizard分别給IDC_W和IDC_D添加CEdit類變量m_ctrlW和m_ctrlD;
-
在 MultiThread8Dlg.cpp檔案中添加如下内容:
為了檔案中能夠正确使用同步類,在檔案開頭添加:#include “afxmt.h”
afxmt是用來建立和管理多線程的。
afxmt.h是一個MFC多線程同步的一個擴充頭檔案, 該頭檔案中聲明了用于MFC程式設計中多線程同步時所需要的類, 比如:
class CSyncObject;
class CSemaphore;
class CMutex;
class CEvent;
class CCriticalSection;
包含該頭檔案, 就可以直接在自己的程式中使用這幾個類!
afxmt名字解釋: afx代表全局的意思, mt是Multi Thread的簡寫
- 定義臨界區和一個字元數組,為了能夠在不同線程間使用,定義為全局變量:CCriticalSection critical_section;char g_Array[10];
/不能在頭檔案中定義,會出現error LNK2005: “class CCriticalSection section” (?section@@3VCCriticalSection@@A) already defined in lab.obj的錯誤,因為連接配接順序的問題,是以頭檔案可以放在任意位置,但是變量的設定必須在cpp檔案中。
- 添加線程函數:
UINT WriteW(LPVOID pParam)
{
CEdit *pEdit=(CEdit*)pParam;
pEdit->SetWindowText("");
critical_section.Lock();
// 鎖定臨界區,其它線程遇到critical_section.Lock();語句時要等待
//直至執行 critical_section.Unlock();語句
for(int i=0;i<10;i++)
{
g_Array[i]=''W'';
pEdit->SetWindowText(g_Array);
Sleep(1000);
}
critical_section.Unlock();
return 0;
}
UINT WriteD(LPVOID pParam)
{
CEdit *pEdit=(CEdit*)pParam;
pEdit->SetWindowText("");
critical_section.Lock();
// 鎖定臨界區,其它線程遇到critical_section.Lock();語句時要等待
//直至執行 critical_section.Unlock();語句
for(int i=0;i<10;i++)
{
g_Array[i]=''D'';
pEdit->SetWindowText(g_Array);
Sleep(1000);
}
critical_section.Unlock();
return 0;
}
- 分别輕按兩下按鈕IDC_WRITEW和IDC_WRITED,添加其響應函數
void CMultiThread8Dlg::OnWritew()
{
CWinThread *pWriteW=AfxBeginThread(WriteW,
&m_ctrlW,
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED);
pWriteW->ResumeThread();
}
void CMultiThread8Dlg::OnWrited()
{
CWinThread *pWriteD=AfxBeginThread(WriteD,
&m_ctrlD,
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED);
pWriteD->ResumeThread();
}
事件 CEvent
- 事件狀态有兩種:有信号狀态、無信号狀态。
- 事件有人工事件和自動事件。人工事件信号狀态必須使用SetEvent手動切換為有信号狀态。用ResetEvent切換為無信号狀态。
- 可以使用WaitForSingleObject設定線程同步。
- 注意線程函數必須有傳回值,否則建立線程時會出現參數錯誤。
CEvent.demo
- 插入button和編輯框1,編輯框2,分别添加CEdit類型變量first,second。
- 添加多線程管理頭檔案afxmt.h。
- 建立CEvent對象 first。
- 添加線程函數:
UINT fun1(LPVOID lpParam){
CEdit* x=(CEdit*)lpParam;
CString str;
for(int i=0;i<10;i++){
str.Format(_T("%d"),i);
Sleep(100);
x->SetWindowText(str);
}
show.SetEvent();
return 0;
}
UINT fun2(LPVOID lpParam){
CEdit* x=(CEdit*)lpParam;
CString str;
WaitForSingleObject(show,INFINITE);
for(int i=0;i<10;i++){
str.Format(_T("%d"),i);
Sleep(100);
x->SetWindowText(str);
}
return 0;
}
- 輕按兩下Button:
void ClabsdfaDlg::OnBnClickedButton1()
{
// TODO: Add your control notification handler code here
AfxBeginThread(fun1,&first);
AfxBeginThread(fun2,&second);
}
互斥量 CMutex
- CMutex和CCriticalSection類似。都可以在程序和線程之間進行通信,但是CCriticalSection在程序間通信時會消耗更多的資源。
- 函數原型
HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, // 安全屬性指針 BOOL bInitialOwner, // 初始擁有者 LPCTSTR lpName // 互斥對象名 );
參數bInitialOwner主要用來控制互斥對象的初始狀态。一般多将其設定為FALSE,以表明互斥對象在建立時并沒有為任何線程所占有。如果在建立互斥對象時指定了對象名,那麼可以在本程序其他地方或是在其他程序通過OpenMutex()函數得到此互斥對象的句柄。
CMutex.demo
- 插入button和編輯框1,編輯框2,分别添加CEdit類型變量first,second。
- 添加多線程管理頭檔案afxmt.h。
- 建立CMutex對象 :
- 添加線程函數:
UINT fun1(LPVOID lpParam){
CEdit* x=(CEdit*)lpParam;
CString str;
WaitForSingleObject(handle,INFINITE);
for(int i=0;i<10;i++){
str.Format(_T("%d"),i);
Sleep(100);
x->SetWindowText(str);
}
ReleaseMutex(handle);
return 0;
}
UINT fun2(LPVOID lpParam){
CEdit* x=(CEdit*)lpParam;
CString str;
WaitForSingleObject(handle,INFINITE);
for(int i=0;i<10;i++){
str.Format(_T("%d"),i);
Sleep(100);
x->SetWindowText(str);
}
ReleaseMutex(handle);
return 0;
}
- 輕按兩下Button:
void ClabsdfaDlg::OnBnClickedButton1()
{
// TODO: Add your control notification handler code here
AfxBeginThread(fun1,&first);
AfxBeginThread(fun2,&second);
}
信号量 CSemaphore
- CSemaphore可以限制變量可以被線程通路的數量。
- 函數原型:
CSemaphore(LONG lInitialCount=1, LONG lMaxCount=1, LPCTSTR pstrName=NULL, LPSECURITY_ATTRIBUTES lpsaAttributes=NULL);
- 每有一個線程通路變量時,則計數值減一,當計數值為0時,其他通路該變量的線程被挂起。當某線程通路結束時,計數值會加1。
CSemaphore.demo
- 建立一個基于對話框的工程,在對話框中加入一個按鈕和三個編輯框控件。
- 給編輯框添加變量:m_ctrlA、m_ctrlB和m_ctrlC;
- 在檔案開頭添加:#include “afxmt.h”
- 添加CSemaphore變量
- 添加線程函數
UINT WriteA(LPVOID pParam)
{
CEdit *pEdit=(CEdit*)pParam;
WaitForSingleObject(semaphoreWrite.m_hObject,INFINITE);
CString str;
for(int i=0;i<10;i++)
{
str.Format(_T("%d"),i);
pEdit->SetWindowText(str);
Sleep(100);
}
ReleaseSemaphore(semaphoreWrite.m_hObject,1,NULL);
return 0;
}
UINT WriteB(LPVOID pParam)
{
CEdit *pEdit=(CEdit*)pParam;
WaitForSingleObject(semaphoreWrite.m_hObject,INFINITE);
CString str;
for(int i=0;i<10;i++)
{
str.Format(_T("%d"),i);
pEdit->SetWindowText(str);
Sleep(100);
}
ReleaseSemaphore(semaphoreWrite.m_hObject,1,NULL);
return 0;
}
UINT WriteC(LPVOID pParam)
{
CEdit *pEdit=(CEdit*)pParam;
WaitForSingleObject(semaphoreWrite.m_hObject,INFINITE);
CString str;
for(int i=0;i<10;i++)
{
str.Format(_T("%d"),i);
pEdit->SetWindowText(str);
Sleep(100);
}
ReleaseSemaphore(semaphoreWrite.m_hObject,1,NULL);
return 0;
}
- 輕按兩下Start按鈕:
void CMultiThread10Dlg::OnBnClickedStart()
{
// TODO: Add your control notification handler code here
AfxBeginThread(WriteA,&m_ctrlA);
AfxBeginThread(WriteB,&m_ctrlB);
AfxBeginThread(WriteC,&m_ctrlC);
}
參考資料
MFC多線程各種線程用法