一、简单的多线程——不需要同步
1.1 带参、不需要同步的多线程
1.1.1 声明线程函数,可以是全局的,也可以是静态的
UINT ThreadFun(LPVOID lParam);
1.1. 2 定义一个结构体(也可以是自定义的类),该结构体的各成员是要传递的参数
struct ThreadInfo{
int nIndex;
BOOL bTag;
};
1.1.3 定义一个结构体成员:
ThreadInfo Info;
1.1.4 在需要启动线程的地方填充结构体成员,并利用AfxBeginThread启动一个线程
Info.nIndex=wParam;
Info.bTag=lParam;
AfxBeginThread(ThreadFun,&Info);
1.1.5 实现线程函数:
UINT ThreadFun(LPVOID lParam)
{
ThreadInfo* pInfo=(ThreadInfo*)lParam;//获取数据
CString strTmp="";
strTmp.Format("%d %d/r/n",pInfo->nIndex,pInfo->bTag);
AfxMessageBox(strTmp);
return 0;
}
1.2 不带参、不需要同步的多线程
1.2.1 声明线程函数:UINT ThreadFun(LPVOID lParam);
1.2.2 在需要启动线程的地方启动线程:AfxBeginThread(ThreadFun,NULL);
1.2.3 实现线程函数:
UINT ThreadFun(LPVOID lParam)
{
AfxMessageBox("不带参、不同步的多线程");
}
二、Win32版多线程——不需要同步
2.1 声明线程函数
DWORD WINAPI Func(LPVOID lParam);
2.2 在需要启动线程的地方启动线程:
HANDLE hThread=CreateThread(NULL,0,Func,NULL,0,NULL);
CloseHandle(hThread);
2.3 实现线程函数:
DWORD WINAPI Func(LPVOID lParam)
{
AfxMessageBox("Win32多线程");
}
三、利用二次消息循环模拟实现多线程:
当应用程序进行复杂计算或占用很多系统资源的操作时,用户点击程序界面按钮时无法响应,有两种解决方法:计算线程,消息循环重载技术,即在应用程序中处理Windows消息循环。这样既可以在主线程中进行复杂计算以满足实时计算要求,又能即使响应用户输入,随时中止计算!
举例如下:
while(!m_bStop && iStep <= 500) {
iStep++;
Sleep(20);
DoEvents(); // 二次消息循环函数
}
void DoEvents()
{
MSG msg;
if (::PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { //从Windows消息队列中取出消息
if (msg.message== WM_QUIT)//如果消息为退出,发送退出消息
{
::PostQuitMessage(-1);
}
if(!AfxGetApp()->PreTranslateMessage(&msg))//如果无法预处理消息
{
::TranslateMessage(&msg);//转换消息
::DispatchMessage(&msg);//发送消息
}
}
AfxGetApp()->OnIdle(0);
//AfxGetApp()->OnIdle(1);//消息队列为空时闲置一段时间
}
四、线程同步
MFC提供了多种同步对象,下面是最常用的四种:
临界区(CCriticalSection)
事件(CEvent)
互斥量(CMutex)
信号量(CSemaphore)
参考文章:http://www.vckbase.com/document/viewdoc/?id=1708
通常在编写多线程程序并需要实现线程同步时,首选临界区,由于他的使用比较简单,而临界区只适用于同一进程中的线程同步,而需要用到不同
进程间的线程同步时,需要使用事件对象或互斥量或信号量。
为了文件中能够正确使用同步类,在文件开头添加:
#include "afxmt.h"
下面的内容假设已经写出了不需要同步的多线程的代码。
4.1 使用临界区--任一时刻只有一个线程可以拥有临界区对象
4.1.1 定义CCriticalSection类的一个全局对象(以使各个线程均能访问)
4.1.2 在访问需要保护的资源或代码之前,调用CCriticalSection类的成员Lock()
4.1.3 访问临界区完毕后,使用CCriticalSection的成员函数Unlock()来释放临界区
4.2 使用事件对象
在MFC中,CEvent 类对象有两种类型:人工事件和自动事件.一个自动CEvent 对象在被至少一个线程释放后会自动返回到无信号状态;而人工事件对象获得信号后,释放可利用线程,但直到调用成员函数ReSetEvent()才将其设置为无信号状态。在创建CEvent 类的对象时,默认创建的是自动事件。
如果CEvent对象为自动事件,则在SetEvent()将事件设置为有信号状态后,CEvent 类对象由系统自动重置为无信号状态。
4.2.1 定义CEvent类的一个全局变量eventWriteD
4.2.2 在启动线程之前调用eventWriteD.SetEvent();
4.2.2 在要保护的资源或代码之前,调用WaitForSingleObject(eventWriteD.m_hObject,INFINITE);
4.2.3 在访问完毕后:eventWriteD.SetEvent();
4.3 使用互斥对象
4.3.1 定义一个全局的HANDLE hMutex;
4.3.2 创建互斥对象:hMutex=CreateMutex(NULL,FALSE,NULL);
4.3.3 在CreateMutext后ReleaseMutex(hMutex);
4.3.4 在需要保护的资源代码之前,调用WaitForSingleObject(hMutex,INFINITE);
4.3.5 在访问完毕后:ReleaseMutex(hMutex);
4.4 信号量——当需要一个计数器来限制可以使用某个线程的数目时,可以使用“信号量”对象
4.4.1 定义一个CSemaphore 类的全局变量semaphoreWrite
4.4.2 在保护代码之前加上:WaitForSingleObject(semaphoreWrite.m_hObject,INFINITE);
4.4.3 在访问完毕后调用:ReleaseSemaphore(semaphoreWrite.m_hObject,1,NULL);