1、事件同步类:
CEvent( BOOL bInitiallyOwn = FALSE, //用来指定事件对象初始状态是否为发信状态
BOOL bManualReset = FALSE, //用来指定创建事件对象是自动事件还是手工事件对象
LPCTSTR lpszName = NULL, //用来定义事件对象的名称
LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );//为指向一个LPSECURITY_ATTRIBUTES结构的指针
CEvent类提供了三种对事件对象进行操控的方法:
BOOL SetEvent( );//设置事件为发信状态,并释放其他正在等待的线程
BOOL PulseEvent( );//设置事件为发信状态,并释放其他正在等待的线程,然后把时间设置为未发信状态
BOOL ResetEvent( );//设置事件为未发信状态
详细理解通过一个例子来了解工作原理:
例子:设计一个应用程序,当用户在程序窗口上按下鼠标左键时,会创建和启动两个线程,这两个线程被启动后,各自显示一个信息框,表明线程已被启动,随即被时间对象的Lock函数把线程挂起,当用户在程序窗口按下鼠标右键时,启动另一个线程,在该线程中把时间对象设置为“发信”状态,从而启动了第一个被挂起的线程。
实现:(1)用MFC创建一个单文档应用程序
(2)为了使用事件对象,在应用程序的头文件中包含afxmt.h
#include"afxmt.h"
(3)在程序的视图类的实现文件(.cpp文件)中定义一个全局事件对象
CEvent eventObj;
(4)在程序的视图类实现文件中编写如下线程函数:
//线程函数1
UINT MessageThread1(LPVOID pParam)
{
char* pMessage="Thread1 is started";//线程一对话框显示的字符串
CWnd* pMainWnd=AfxGetMainWnd();//获取主窗口的指针,在这里主窗口的视图类的主窗口
::MessageBox(pMainWnd->m_hWnd,//M每一个窗口类当中有一个句柄的数据成员
pMessage,//显示的内容
"Thread Message",//对话框的标题
MB_OK
);
eventObj.Lock();//将线程挂起,处于等待状态
pMessage="Thread1 is unblocked";//线程一对话框显示的字符串
//CWnd* pMainWnd=AfxGetMainWnd();//获取主窗口的指针,在这里主窗口的视图类的主窗口
::MessageBox(pMainWnd->m_hWnd,//M每一个窗口类当中有一个句柄的数据成员
pMessage,//显示的内容
"Thread Message",//对话框的标题
MB_OK
);
eventObj.Lock();//将线程挂起,处于再次等待状态
pMessage="Thread1 is unblocked again";//线程一对话框显示的字符串
//CWnd* pMainWnd=AfxGetMainWnd();//获取主窗口的指针,在这里主窗口的视图类的主窗口
::MessageBox(pMainWnd->m_hWnd,//M每一个窗口类当中有一个句柄的数据成员
pMessage,//显示的内容
"Thread Message",//对话框的标题
MB_OK
);
return 0;
}
//线程函数2
UINT MessageThread2(LPVOID pParam)
{
char * pMessage="Thread2 is started";//线程一对话框显示的字符串
CWnd* pMainWnd=AfxGetMainWnd();//获取主窗口的指针,在这里主窗口的视图类的主窗口
::MessageBox(pMainWnd->m_hWnd,//M每一个窗口类当中有一个句柄的数据成员
pMessage,//显示的内容
"Thread Message",//对话框的标题
MB_OK
);
eventObj.Lock();//将线程挂起,处于等待状态
pMessage="Thread2 is unblocked";//线程一对话框显示的字符串
//CWnd* pMainWnd=AfxGetMainWnd();//获取主窗口的指针,在这里主窗口的视图类的主窗口
::MessageBox(pMainWnd->m_hWnd,//M每一个窗口类当中有一个句柄的数据成员
pMessage,//显示的内容
"Thread Message",//对话框的标题
MB_OK
);
return 0;
}
//线程函数3
UINT MessageThread3(LPVOID pParam)
{
eventObj.SetEvent();//将线程设置为事件设置为发信状态
return 0;
}
(5)鼠标消息响应函数如下
// CThreadSyncView 消息处理程序
void CThreadSyncView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
AfxBeginThread(MessageThread1,"Thread is stated");
AfxBeginThread(MessageThread2,"Thread is stated");
CView::OnLButtonDown(nFlags, point);
}
void CThreadSyncView::OnRButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
AfxBeginThread(MessageThread3,"Thread is unblocked");
CView::OnRButtonDown(nFlags, point);
}
2、临界段同步类(CCriticalSectiond)
类CCriticalSectiond的对象叫做临界段,他只允许一个线程占有某个共享资源,因此这个类的对象是用来保护独占式共享资源的。
详细细节同样通过一个例子来理解:
例子:使用临界段写一个有两个线程的应用程序
实现:(1)用MFC创建一个单文档的应用程序
(2)在应用程序的头文件中添加afxmt.h头文件
#include<afxmt.h>
(3)在视图类的实现文件中定义一个临界段的对象
CCriticalSection criticalSection;
(4)在视图类实现文件中定义两个线程函数
//定义线程函数
UINT MessageThread1(LPVOID pParam)//线程1
{
criticalSection.Lock();//设置临界段,此时一直执行该线程
char * pMessage="Thread1 is started";
CWnd * pMainWnd=AfxGetMainWnd();//这是线程函数获取视类的指针,而每一个视类的对象都包含一个该类的句柄成员
::MessageBox(pMainWnd->m_hWnd,//视类的句柄
pMessage,//显示的内容
"Thread message",//对话框的标题
MB_OK);
criticalSection.Unlock();//线程释放临界段
return 0;
}
UINT MessageThread2(LPVOID pParam)//线程1
{
criticalSection.Lock();//设置临界段,此时一直执行该线程
char * pMessage="Thread2 is started";
CWnd * pMainWnd=AfxGetMainWnd();//这是线程函数获取视类的指针,而每一个视类的对象都包含一个该类的句柄成员
::MessageBox(pMainWnd->m_hWnd,//视类的句柄
pMessage,//显示的内容
"Thread message",//对话框的标题
MB_OK);
criticalSection.Unlock();//线程释放临界段
return 0;
}
(5)在视图类鼠标消息响应函数中编写如下代码
// CCriticalSectionView 消息处理程序
void CCriticalSectionView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
//单击左键就会启动两个线程
AfxBeginThread(MessageThread1,"Thread is starting");//开启线程1
AfxBeginThread(MessageThread2,"Thread is starting");//开启线程2
CView::OnLButtonDown(nFlags, point);
}
3、互斥体类(CMutex)
互斥体是CMutex类的对象,也只允许一个线程占有某个共享资源,以保护独占式共享资源。因此互斥体的使用方法与临界段的使用方法极为相似,所不同的是临界段只能在同一个进程中对线程进行同步,而互斥体可以在不同的进程中进行线程同步控制。
CMutex( BOOL bInitiallyOwn = FALSE,//用来指定互斥体对象的初始状态时锁定的还是非锁定的
LPCTSTR lpszName = NULL,//用来指定互斥体对象的名称
LPSECURITY_ATTRIBUTES lpsaAttribute = NULL //一个指向 LPSECURITY_ATTRIBUTES结构体指针
);
详细过程通过一个例子来理解:
例子:编写一个应用程序,实现进程间线程的同步
实现:(1)用MFC创建一个单文档的应用程序
(2)在应用程序的头文件中包含头文件afxmt.h
#include<afxmt.h>
(3)在视图类的实现文件中定义一个互斥体对象
CMutex mutexObj(FALSE,"mutex1");
(4)在视图类的实现文件中定义线程函数
//定义线程函数
UINT MessageThread1(LPVOID pParam)//线程函数1
{
//线程一旦开启,立刻用互斥体对象来锁定
mutexObj.Lock();//锁定线程
char* pMessage="Thread1 is started";
//线程开启后在视类窗口区内显示一个对话框,需要获取视类句柄,而线程是在视类添加的,因此只需要获取线程的主窗口的句柄
CWnd* pMainCwnd=AfxGetMainWnd();//获取主窗口指针,该指针包含主窗口句柄的成员
//显示对话框
::MessageBox(pMainCwnd->m_hWnd,//主窗口句柄
pMessage,//显示的内容
"Thread message",//对话框的标题
MB_OK//对话框有ok按钮
);
//完了之后就互斥体解锁,让给其他线程来运行
mutexObj.Unlock();
return 0;
}
UINT MessageThread2(LPVOID pParam)//线程函数2
{
//线程一旦开启,立刻用互斥体对象来锁定
mutexObj.Lock();//锁定线程
char* pMessage="Thread2 is started";
//线程开启后在视类窗口区内显示一个对话框,需要获取视类句柄,而线程是在视类添加的,因此只需要获取线程的主窗口的句柄
CWnd* pMainCwnd=AfxGetMainWnd();//获取主窗口指针,该指针包含主窗口句柄的成员
//显示对话框
::MessageBox(pMainCwnd->m_hWnd,//主窗口句柄
pMessage,//显示的内容
"Thread message",//对话框的标题
MB_OK//对话框有ok按钮
);
//完了之后就互斥体解锁,让给其他线程来运行
mutexObj.Unlock();
return 0;
}
(5)在鼠标左键按下事件响应函数中添加如下代码
void CMutexView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
//左键单击是为了启动线程
AfxBeginThread(MessageThread1,"thread1 is starting");
AfxBeginThread(MessageThread2,"thread2 is starting");
CView::OnLButtonDown(nFlags, point);
}
4、信号量类(CSemaphore)
信号量类是CSemaphore的对象,该对象的作用是对访问某个共享资源的线程的数目进行控制。
信号量的构造函数:
CSemaphore( LONG lInitialCount = 1, //用来指定设定计数器的初始值
LONG lMaxCount = 1, //用来设定计数器的最大计数值
LPCTSTR pstrName = NULL,
LPSECURITY_ATTRIBUTES lpsaAttributes = NULL
);
详细过程通过一个例子来理解
例子:设计一个有四个线程的应用程序,理解信号量对象的使用
实现:(1)用MFC创建一个单文档的应用程序
(2)在应用程序的头文件中包含afxmt.h
#include<afxmt.h>
(3)在视图类的实现文件中定义一个信号量对象
CSemaphore semaphorObj(2,3);
(4)在视图类的实现文件中定义四个线程函数
//定义线程函数
UINT MessageThread1(LPVOID pParam)//线程函数1
{
//一旦启动线程,则将该线程绑定相应的资源
semaphoreObj.Lock();
//用对话框的方式来提示
char *pMessage="Thread1 is runing!!!";
//获取线程的主窗口
CWnd* pMainCWnd=AfxGetMainWnd();
//弹出对话框
::MessageBox(
pMainCWnd->m_hWnd,//主窗口句柄
pMessage,//提示的内容
"Thread message",//对话框的标题
MB_OK
);
//提示完了之后就解锁
semaphoreObj.Unlock();
return 0;
}
UINT MessageThread2(LPVOID pParam)//线程函数1
{
//一旦启动线程,则将该线程绑定相应的资源
semaphoreObj.Lock();
//用对话框的方式来提示
char *pMessage="Thread2 is runing!!!";
//获取线程的主窗口
CWnd* pMainCWnd=AfxGetMainWnd();
//弹出对话框
::MessageBox(
pMainCWnd->m_hWnd,//主窗口句柄
pMessage,//提示的内容
"Thread message",//对话框的标题
MB_OK
);
//提示完了之后就解锁
semaphoreObj.Unlock();
return 0;
}
UINT MessageThread3(LPVOID pParam)//线程函数1
{
//一旦启动线程,则将该线程绑定相应的资源
semaphoreObj.Lock();
//用对话框的方式来提示
char *pMessage="Thread3 is runing!!!";
//获取线程的主窗口
CWnd* pMainCWnd=AfxGetMainWnd();
//弹出对话框
::MessageBox(
pMainCWnd->m_hWnd,//主窗口句柄
pMessage,//提示的内容
"Thread message",//对话框的标题
MB_OK
);
//提示完了之后就解锁
semaphoreObj.Unlock();
return 0;
}
UINT MessageThread4(LPVOID pParam)//线程函数1
{
//一旦启动线程,则将该线程绑定相应的资源
semaphoreObj.Lock();
//用对话框的方式来提示
char *pMessage="Thread4 is runing!!!";
//获取线程的主窗口
CWnd* pMainCWnd=AfxGetMainWnd();
//弹出对话框
::MessageBox(
pMainCWnd->m_hWnd,//主窗口句柄
pMessage,//提示的内容
"Thread message",//对话框的标题
MB_OK
);
//提示完了之后就解锁
semaphoreObj.Unlock();
return 0;
}
(5)在鼠标左键按下的消息响应函数当中添加
// CSemaphoreView 消息处理程序
void CSemaphoreView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
//单击开启线程
AfxBeginThread(MessageThread1,NULL);
AfxBeginThread(MessageThread2,NULL);
AfxBeginThread(MessageThread3,NULL);
AfxBeginThread(MessageThread4,NULL);
CView::OnLButtonDown(nFlags, point);
}