天天看點

線程同步緒論臨界區 CCriticalSection事件 CEvent互斥量 CMutex信号量 CSemaphore參考資料

目錄

  • 緒論
  • 臨界區 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多線程各種線程用法

繼續閱讀