目录
绪论
线程同步有四种方式:
- 临界区(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对象 :
HANDLE handle=CreateMutex(NULL,FALSE,(LPCTSTR )"m");
-
添加线程函数:
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变量
CSemaphore semaphoreWrite(2,2);
- 添加线程函数
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);
}