MFC多线程编程
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
MFC多线程编程
1. 线程的基本概念
进程和线程都是操作系统的概念。
进程是应用程序的执行实例。
每个进程是由私有的虚拟地址空间、代码、数据和其它系统资源组成的。
进程在运行过程中创建的资源随着进程的终止而被销毁,所使用的系统资源在进程终止时被释放或关闭。
线程是进程内部的一个执行单元。
系统创建进程后,实际上就是启动了该进程的主执行线程,它以函数地址的形式(比如main或WinMain函数),将程序的启动点提供给Windows系统。
主执行线程终止了,进程也就随之终止。
每个进程至少有一个主执行线程,它无需由用户主动创建,而是由系统自动创建的。
用户根据需要在应用程序中创建其它线程,多个线程并发地运行于同一个进程中。
一个进程中的所有线程都在该进程的虚拟地址空间中,共同使用这些虚拟地址空间、全局变量和系统资源,所以线程间的通讯非常方便,多线程技术的应用也较为广泛。
多线程可以实现并行处理,避免了某项任务长时间占用CPU。
单CPU运行多线程时,操作系统为每个线程安排一些CPU 时间,轮换方式向线程提供时间片,这就给人一种假象,似乎这些线程在同时运行。
由此可见,如果两个非常活跃的线程为了抢夺CPU的控制权,在线程切换时会消耗很多CPU资源,反而会降低系统性能。
这点要在编程时注意。
创建线程共三种方法:
(1)WIN32 API的CreateThread()
(2)MFC全局函数AfxBeginThread()
(3)CWinThread::CreateThread()
2. WIN32 API多线程
2.1 WIN32 API多线程函数
1.HANDLE CreateThread(//方法一
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId);
2.DWORD SuspendThread(HANDLE hThread); //挂起指定的线程
3.DWORD ResumeThread(HANDLE hThread); //结束线程的挂起状态,执行线程
4.VOID ExitThread(DWORD dwExitCode); //主要在线程的执行函数中被调用,终结线程自身的执行
5.BOOL TerminateThread(HANDLE hThread,DWORD dwExitCode); //强行终止线程,一般不建议使用:(1)是不安全的,
可能会引起系统不稳定;(2)虽然该函数立即终止线程,但并不释放线程所占用的资源。
6.BOOL PostThreadMessage(
DWORD idThread,
UINT Msg,
WPARAM wParam,
LPARAM lParam); //将一条消息放到指定线程的消息队列中,并且不等到消息被该线程处理时便返回。
如果接收
消息的线程没有创建消息循环,则该函数执行失败。
7.DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); //功能:在某一线程(创建子线程的线程)
中调用,线程暂时挂起,系统监视hHandle(子线程句柄)所指向的对象的状态。
返回:如果在挂起的dwMilliseconds毫秒内,线程所等待的对象变为有信号状态,则该函数立即返回;如果超时时间已经到达dwMilliseconds毫秒,但hHandle 所指向的对象还没有变成有信号状态,函数照样返回。
参数dwMilliseconds有两个特殊值:0和INFINITE。
若为0,立即返回;若为INFINITE,则线程一直被挂起,直到hHandle所指向的对象变为有信号状态。
2.2 例程
MultiThread1
使用Win32 API编写多线程。
1.建立基于对话框的工程,在对话框中加入两个按钮和一个编辑框,两个按钮的ID分别是IDC_START、IDC_STOP ,标题
分别为“启动”,“停止”,IDC_STOP的属性选中Disabled;编辑框IDC_TIME 的属性选中Readonly;
2.在MultiThread1Dlg.h中添加线程函数声明void ThreadFunc();(应在类CMultiThread1Dlg声明外部)
3.在类CMultiThread1Dlg内部添加protected型变量:
HANDLE hThread; //线程句柄
DWORD ThreadID; //线程ID
4.在MultiThread1Dlg.cpp文件中添加全局变量m_bRun:
volatile BOOL m_bRun; //代表线程是否正在运行
volatile修饰符:告诉编译器无需对该变量作任何的优化,即无需将它放到一个寄存器中,并且该值可被外部改变。
对于多线程引用的全局变量来说,volatile是一个非常重要的修饰符;
5.编写代码:
IDC_START的消息函数:
void CMultiThread1Dlg::OnStart(){
// TODO: Add your control notification handler code here
hThread=CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)ThreadFunc,
NULL,
0,
&ThreadID);
GetDlgItem(IDC_START)->EnableWindow(FALSE);
GetDlgItem(IDC_STOP)->EnableWindow(TRUE);
}
IDC_STOP的消息函数:
void CMultiThread1Dlg::OnStop(){
// TODO: Add your control notification handler code here
m_bRun=FALSE;
GetDlgItem(IDC_START)->EnableWindow(TRUE);
GetDlgItem(IDC_STOP)->EnableWindow(FALSE);
}
线程函数:
void ThreadFunc(){
CTime time;
CString strTime;
m_bRun=TRUE; //m_bRun==TRUE,线程一直运行
while(m_bRun) {
time=CTime::GetCurrentTime();
strTime=time.Format("%H:%M:%S");
::SetDlgItemText(::FindWindow(NULL, L"MultiThread1"),IDC_TIME,strTime);
//::SetDlgItemText(AfxGetApp()->m_pMainWnd->m_hWnd,IDC_TIME,strTime); //也能满足要求
//::SetDlgItemText(AfxGetMainWnd()->m_hWnd,IDC_TIME,strTime);//VS2010中画线代码不能获取窗口句柄!
Sleep(1000);
}
}
MultiThread2
(1)如何将一个整型参数传送到一个线程;
(2)如何等待一个线程完成处理。
1.建立一个基于对话框的工程,在对话框中加入一个编辑框和一个按钮,ID分别是IDC_COUNT,IDC_START ,按钮控件
的标题为“开始”;
2.在MultiThread2Dlg.h中添加线程函数声明:void ThreadFunc(int integer);(在类CMultiThread2Dlg声明外部)
3.在类CMultiThread2Dlg内部添加protected型变量:
HANDLE hThread; //线程句柄
DWORD ThreadID; //线程ID
4.利用ClassWizard为编辑框IDC_COUNT添加intm_nCount;
5.编写代码:
IDC_START的消息函数:
void CMultiThread2Dlg::OnStart(){
UpdateData(TRUE);
int integer=m_nCount;
hThread=CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)ThreadFunc,
(VOID*)integer,
0,
&ThreadID);
GetDlgItem(IDC_START)->EnableWindow(FALSE);
WaitForSingleObject(hThread,INFINITE);
GetDlgItem(IDC_START)->EnableWindow(TRUE);
}
线程函数:
void ThreadFunc(int integer){
int i;
for(i=0;i<integer;i++){
Beep(200,50);
Sleep(1000);
}
}
MultiThread3
如何将一个结构体指针传送给子线程。
1.建立一个基于对话框的工程,在对话框中加入一个编辑框IDC_MILLISECOND,一个按钮IDC_START,标题为“开始”,
一个进度条IDC_PROGRESS1;
2.为IDC_MILLISECOND添加intm_nMilliSecond,为IDC_PROGRESS1添加CProgressCtrlm_ctrlProgress;
3.在MultiThread3Dlg.h中添加一个结构体定义:(在类CMultiThread3Dlg声明外部)
struct threadInfo{
UINT nMilliSecond;
CProgressCtrl* pctrlProgress;
};
4.在MultiThread3Dlg.h文件中添加线程函数声明:UINT ThreadFunc(LPVOID lpParam);(在类CMultiThread3Dlg声明外部)
5.在类CMultiThread3Dlg内部添加protected型变量:
HANDLE hThread;
DWORD ThreadID;
6.在MultiThread3Dlg.cpp文件中定义公共变量threadInfo Info;
7.编写代码:
IDC_START的消息函数:
void CMultiThread3Dlg::OnStart(){
// TODO: Add your control notification handler code here
UpdateData(TRUE);
Info.nMilliSecond=m_nMilliSecond;
Info.pctrlProgress=&m_ctrlProgress;
hThread=CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)ThreadFunc,
&Info,
0,
&ThreadID);
/*GetDlgItem(IDC_START)->EnableWindow(FALSE);
WaitForSingleObject(hThread,INFINITE);
GetDlgItem(IDC_START)->EnableWindow(TRUE);
*/
/*如果执行上面/* */语句,编译运行,程序停止反应,这是因为线程死锁。
因为WaitForSingleObject函数会将主线程挂起(任何消息都得不到处理),而子线程ThreadFunc正在设置进度条,它等主线程将进度条刷新的消息处理完后才会检测通知事件,这样两个线程都在互相等待,死锁发生了,编程时应注意避免。
*/
}
在BOOL CMultiThread3Dlg::OnInitDialog()中添加代码:
{
……
// TODO: Add extra initialization here
m_ctrlProgress.SetRange(0,99);
m_nMilliSecond=10;
UpdateData(FALSE);
return TRUE;// return TRUE unless you set the focus to a control
}
线程函数:
UINT ThreadFunc(LPVOID lpParam){
threadInfo* pInfo=(threadInfo*)lpParam;
for(int i=0;i<100;i++){
int nTemp=pInfo->nMilliSecond;
pInfo->pctrlProgress->SetPos(i);
Sleep(nTemp);
}
return 0;
}
测试在Windows下最多可创建线程的数目。
1.建立一个基于对话框的工程,在对话框中加入一个按钮IDC_TEST,标题为“测试”,一个编辑框IDC_COUNT,属性选
中Read-only;
2.在MultiThread4Dlg.cpp中添加公共变量volatile BOOL m_bRunFlag=TRUE; //表示是否还能继续创建线程
3.为IDC_COUNT添加intm_nCount;
4.在MultiThread4Dlg.h中添加:DWORD WINAPI threadFunc(LPVOID threadNum);(在类CMultiThread4Dlg外部)
5.编写代码:
IDC_TEST的消息函数:
void CMultiThread4Dlg::OnTest(){
DWORD threadID;
GetDlgItem(IDC_TEST)->EnableWindow(FALSE);
long nCount=0;
while(m_bRunFlag){ //不断创建线程,直到再不能创建为止
if(NULL==CreateThread(NULL,0,threadFunc,NULL,0,&threadID)){
m_bRunFlag=FALSE;
break;
}
else{
nCount++;
}
}
m_nCount=nCount;
UpdateData(FALSE);
Sleep(5000); //延时5秒,等待所有创建的线程结束
GetDlgItem(IDC_TEST)->EnableWindow(TRUE);
m_bRunFlag=TRUE;
}
线程函数:
DWORD WINAPI threadFunc(LPVOID threadNum){
while(m_bRunFlag){
Sleep(3000);
}
return 0;
}
3. MFC多线程
MFC中有两类线程:(1)工作者线程;(2)用户界面线程。
二者主要区别:(1)没有消息循环,(2)有自己的消息队列和消息循环。
(1)通常用来执行后台计算和维护任务,如冗长的计算,后台打印机打印等。
(2)一般用于处理独立于其他线程之外的用户输入,响应用户及系统所产生的事件和消息等。
3.1.1 MFC中创建线程
1MFC全局函数AfxBeginThread():它有两种重载形式,分别用于创建工作者线程和用户界面线程://方法二
(1) CWinThread* AfxBeginThread(
AFX_THREADPROC pfnThreadProc, //工作者线程函数指针UINT ExecutingFunction(LPVOID pParam);其返回值为0 LPVOID pParam,
nPriority=THREAD_PRIORITY_NORMAL,
UINT nStackSize=0,
DWORD dwCreateFlags=0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);
(2) CWinThread*AfxBeginThread(
CRuntimeClass* pThreadClass,
int nPriority=THREAD_PRIORITY_NORMAL,
UINT nStackSize=0,
DWORD dwCreateFlags=0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);
2CWinThread类:(1)创建CWinThread类的一个对象,(2)调用CWinThread::CreateThread()启动线程://方法三
(1) BOOL CWinThread::CreateThread(
DWORD dwCreateFlags=0,
UINT nStackSize=0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);
(2) virtual BOOL CWinThread::InitInstance(); //用于用户界面线程的初始化;工作者线程一般不用
(3) virtual int CWinThread::ExitInstance();//线程终结前进行一些清理工作;通常用于用户界面线程
CWinThread类的用法:
A.创建用户界面线程的步骤:
1.创建类CWinThread的派生类(以CUIThread类为例)
2.重载InitInstance()与ExitInstance():
BOOL CUIThread::InitInstance(){
CFrameWnd* wnd=new CFrameWnd;
wnd->Create(NULL,"UI Thread Window");
wnd->ShowWindow(SW_SHOW);
wnd->UpdateWindow();
m_pMainWnd=wnd;
return TRUE;
}
3.创建用户界面线程:
void CUIThreadDlg::OnButton1(){
CUIThread* pThread=new CUIThread();
pThread->CreateThread();
}
用户界面线程的执行次序与应用程序主线程相同:
a)调用用户界面线程类的InitInstance();
b)若InitInstance()返回TRUE,则调用Run(),运行一个标准的消息循环:
线程空闲时调用OnIdle();
收到WM_QUIT消息中断线程。
c)Run()返回时,MFC调用ExitInstance()清理资源。
B.创建没有界面而有消息循环的线程:(这种方法可以完成一个工作者线程的功能)
1.从CWinThread派生一个新类;
2.在InitInstance()中不创建界面并最后返回FALSE,这表示仅执行InitInstance()中的任务而不执行消息循环。
3.1.2线程间通信
线程通信:两个线程之间信息传递的渠道。
1. 全局变量
由于同一进程的各个线程共享该进程的资源,故线程通信最简单的方法是使用全局变量。
对于标准类型的全局变量,建议使用volatile修饰符,它告诉编译器无需对变量作任何优化:无需将它放到寄存器中,且该值可被外部改变。
如果线程间传递的信息较复杂,可以定义一个结构体,通过结构体的指针进行信息传递。
2. 自定义消息
可以在线程函数中向另一线程发送自定义消息来进行线程通信。
一个线程向另一线程发送消息是通过操作系统实现的。
Windows的消息驱动机制:当一个线程发出一条消息时,Windows首先接收到该消息,然后把该消息转发给目标线程,目标线程必须已经建立了消息循环。
3.1.3线程的同步
线程的同步:使隶属于同一进程的各个线程协调一致地工作。
为什么要进行线程的同步?
虽然多线程会带来诸多好处,但也有问题需要解决:例如,磁盘驱动器是独占性的系统资源,由于线程可以执行进程的任何代码段,且线程的运行是由系统自动调度完成的,具有一定的不确定性,因此就可能出现两个线程同时对磁盘驱动器进行操作的错误。
MFC主要提供的四种线程同步对象:
A.临界区(CCriticalSection)
B.互斥量(CMutex)
C.事件(CEvent)
D.信号量(CSemaphore)
3.1.3.1CCriticalSection
某一时刻只有一个线程可以拥有CriticalSection对象,该线程可以访问被保护起来的资源或代码段,其他希望进入CriticalSection的线程被挂起,直到该线程放弃CriticalSection为止。
这样就保证了在同一时刻不会出现多个线程访问共享资源的情况。
其用法如下:
1.定义CCriticalSection类的一个全局对象(以使各个线程均能访问);
2.在需要保护的资源或代码之前,调用CCriticalSection::Lock()获得临界区对象;
3.访问临界区完毕后,调用CCriticalSection::Unlock()来释放临界区。
3.1.3.2CMutex
Mutex对象与CriticalSection对象很相似,其不同在于:前者可以在进程间使用,而后者只能在同一进程的各线程间使用,但它比前者更节省系统资源,更有效率。
3.1.3.3CEvent
CEvent支持事件。
事件:是一种同步对象,它在一个线程发生某种情况时,唤醒另一线程。
例如:在某些网络Apps中,一个线程A负责监听通讯端口,另一线程B负责更新用户数据。
通过使用CEvent 类,A可以通知B何时更新用户数据。
CEvent对象有两种状态:有信号和无信号。
线程监视其CEvent对象的状态,并在相应的时候采取相应的操作。
MFC中CEvent有两种类型:自动事件和人工事件。
CEvent类默认创建的是前者,它被至少一个线程释放后,自动返回无信号;后者需要调用ReSetEvent()才能设置为无信号。
CEvent成员函数:
1.CEvent(
BOOL bInitiallyOwn=FALSE, //指定事件对象初始化状态:TRUE为有信号
BOOL bManualReset=FALSE, //指定要创建的是人工事件还是自动事件:TRUE为人工事件
LPCTSTR lpszName=NULL, //一般为NULL
LPSECURITY_ATTRIBUTES lpsaAttribute=NULL); //一般为NULL
2.BOOL CEvent::SetEvent();
//对于人工事件,它将CEvent对象保持为有信号,直到调用ResetEvent()为止;
//对于自动事件,它将CEvent对象设置为有信号,CEvent 对象由系统自动重置为无信号。
3.BOOL CEvent::ResetEvent();
//它将事件设置为无信号,并保持该状态直至调用SetEvent()为止。
//自动事件不需要调用它。
//一般通过WaitForSingleObject()监视事件状态。
3.1.3.4CSemaphore
CSemaphore提供了访问共享资源线程的计数,它能限制访问共享资源线程的数目。
CSemaphore构造函数:
CSemaphore (
LONG lInitialCount=1, //当前可用资源计数(A)
LONG lMaxCount=1, //最大资源计数(B)
LPCTSTR pstrName=NULL, //一般为NULL
LPSECURITY_ATTRIBUTES lpsaAttributes=NULL); //一般为NULL
一般情况下:将A设置为B。
共享资源每增加一个线程的访问,A减1;只要A大于0,其他线程可以访问该资源,信号量信号可以发出。
当A减小到0时,说明占用该资源的线程已达最大数目,其他线程不能访问该资源,信号量信号不能发出;所有尝试访问该资源的线程都被放到一个队列中等待,直到超时或A不为零。
线程处理完共享资源后,应调用ReleaseSemaphore()将A加1。
3.2 例程
MultiThread5
利用AfxBeginThread()实现例程MultiThread3。
1.建立一个基于对话框的工程,在对话框中加入一个编辑框IDC_MILLISECOND,一个按钮IDC_START,标题为“开始”,
一个进度条IDC_PROGRESS1;
2.为IDC_MILLISECOND添加intm_nMilliSecond,为进度条IDC_PROGRESS1添加CProgressCtrlm_ctrlProgress;
3.在MultiThread5Dlg.h中添加结构定义:(在类CMultiThread5Dlg的外部)
struct threadInfo{
UINT nMilliSecond;
CProgressCtrl* pctrlProgress;
};
4.线程函数声明:UINT ThreadFunc(LPVOID lpParam);(在类CMultiThread5Dlg的外部)
5.在类CMultiThread5Dlg内部添加protected型变量:CWinThread* pThread;
6.在MultiThread5Dlg.cpp中定义公共变量:threadInfo Info;
7.编写代码:
IDC_START的消息函数:
void CMultiThread5Dlg::OnStart(){
// TODO: Add your control notification handler code here
UpdateData(TRUE);
Info.nMilliSecond=m_nMilliSecond;
Info.pctrlProgress=&m_ctrlProgress;
pThread=AfxBeginThread(ThreadFunc,&Info);
}
在BOOL CMultiThread3Dlg::OnInitDialog()中添加代码:
{
……
// TODO: Add extra initialization here
m_ctrlProgress.SetRange(0,99);
m_nMilliSecond=10;
UpdateData(FALSE);
return TRUE;// return TRUE unless you set the focus to a control
}
线程函数:
UINT ThreadFunc(LPVOID lpParam){
threadInfo* pInfo=(threadInfo*)lpParam;
for(int i=0;i<100;i++){
int nTemp=pInfo->nMilliSecond;
pInfo->pctrlProgress->SetPos(i);
Sleep(nTemp);
}
return 0;
}
MultiThread6
创建用户界面线程。
1.建立一个基于对话框的工程,在对话框中加入一个按钮IDC_UI_THREAD,标题为“用户界面线程”;
2.为工程添加CWinThread的派生类CUIThread;
代码:
class CUIThread : public CWinThread
{
DECLARE_DYNCREATE(CUIThread)
protected:
CUIThread(); //动态创建所使用的受保护的构造函数
virtual ~CUIThread();
public:
virtual BOOL InitInstance();
virtual int ExitInstance();
protected:
CUIThreadDlg m_dlg;
protected:
DECLARE_MESSAGE_MAP()
};
3.给工程添加新对话框IDD_UITHREADDLG,标题为“线程对话框”;
4.为IDD_UITHREADDLG创建一个CDialog的派生类CUIThreadDlg;
5.在UIThread.h中添加:#include "UIThreadDlg.h"
6.在CUIThread类中添加protected变量:CUIThreadDlg m_dlg;
7.在MultiThread6Dlg.cpp中添加:#include "UIThread.h"
8.编写代码:
重载InitInstance():
BOOL CUIThread::InitInstance(){
m_dlg.Create(IDD_UITHREADDLG);
m_dlg.ShowWindow(SW_SHOW);
m_pMainWnd=&m_dlg;
return TRUE;
}
重载ExitInstance():
int CUIThread::ExitInstance(){
m_dlg.DestroyWindow();
return CWinThread::ExitInstance();
}
IDC_UI_THREAD的消息函数:
void CMultiThread6Dlg::OnUiThread(){
CWinThread *pThread=AfxBeginThread(RUNTIME_CLASS(CUIThread));
//另一种方法:CUIThread * pThread = new CUIThread;pThread->CreateThread(); //要求CUIThread的构造函数为public }
MultiThread7
使用自定义消息进行线程通信。
1.建立一个基于对话框的工程,在对话框中加入三个单选按钮IDC_RADIO1,IDC_RADIO2,IDC_RADIO3,标题分别为
1+2+3+4+......+10,1+2+3+4+......+50,1+2+3+4+......+100。
加入按钮IDC_SUM,标题为“求和”。
加入标签框IDC_STATUS,属性选中“边框”;
2.在MultiThread7Dlg.h中定义变量:
protected:
int nAddend; //代表加数大小
3.在MultiThread7Dlg.h中添加:
#include "CalculateThread.h"
#define WM_DISPLAY WM_USER+1
4.在MultiThread7Dlg.h中添加:
CCalculateThread* m_pCalculateThread;
5.为工程添加CWinThread 的派生类CCalculateThread;
6.在文件CalculateThread.h 中添加:
#define WM_CALCULATE WM_USER+2
class CCalculateThread : public CWinThread{
……
protected:
afx_msg void OnCalculate(UINT wParam,LONG lParam); //VS2010中为void类型,VC++6.0需为LONG类型……
7.在CalculateThread.cpp中添加:
#include "MultiThread7Dlg.h"
8.编写代码:
三个单选按钮的消息函数:
void CMultiThread7Dlg::OnRadio1(){
nAddend=10;
}
void CMultiThread7Dlg::OnRadio2(){
nAddend=50;
}
void CMultiThread7Dlg::OnRadio3(){
nAddend=100;
}
在OnInitDialog()中添加:
BOOL CMultiThread7Dlg::OnInitDialog(){
……
((CButton*)GetDlgItem(IDC_RADIO1))->SetCheck(TRUE);
nAddend=10;
……
在MultiThread7Dlg.cpp中添加:
BEGIN_MESSAGE_MAP(CMultiThread7Dlg, CDialog)
……
ON_MESSAGE(WM_DISPLAY,OnDisplay)
END_MESSAGE_MAP()
LRESULT CMultiThread7Dlg::OnDisplay(WPARAM wParam,LPARAM lParam){ //处理WM_DISPLAY消息int nTemp=(int)wParam;
SetDlgItemInt(IDC_STATUS,nTemp,FALSE);
return 0;
}
IDC_SUM的消息函数:
void CMultiThread7Dlg::OnSum(){ //建立CalculateThread线程,延时给该线程发送WM_CALCULATE消息m_pCalculateThread= (CCalculateThread*)AfxBeginThread(RUNTIME_CLASS(CCalculateThread));
Sleep(500);
m_pCalculateThread->PostThreadMessage(WM_CALCULATE,nAddend,NULL);
}
在CalculateThread.cpp中添加:
void CCalculateThread::OnCalculate(UINT wParam,LONG lParam){ //为CCalculateThread类添加了WM_CALCULATE消息int nTmpt=0;
for(int i=0;i<=(int)wParam;i++){
nTmpt=nTmpt+i;
}
Sleep(500);
::PostMessage((HWND)(GetMainWnd()->GetSafeHwnd()),WM_DISPLAY,nTmpt,NULL); //向主线程发WM_DISPLAY消息return 0;
}
BEGIN_MESSAGE_MAP(CCalculateThread, CWinThread)
//{{AFX_MSG_MAP(CCalculateThread)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
ON_THREAD_MESSAGE(WM_CALCULATE,OnCalculate) //注意其和主线程的区别
END_MESSAGE_MAP()
MultiThread8
演示CCriticalSection用法。
1.建立一个基于对话框的工程MultiThread8,在对话框中加入两个按钮IDC_WRITEW和IDC_WRITED,其标题分别为“W”
和“D”,再加两个编辑框IDC_W和IDC_D,属性都选中Read-only;
2.在MultiThread8Dlg.h中添加:
UINT WriteW(LPVOID pParam);
UINT WriteD(LPVOID pParam);
3.给IDC_W和IDC_D添加CEdit变量m_ctrlW和m_ctrlD;
4.在MultiThread8Dlg.cpp中添加:
#include "afxmt.h" //为了能正确使用同步类
CCriticalSection critical_section; //为了能在不同线程间使用,定义为全局变量
TCHAR g_Array[10]; //共享资源。
线程W拥有该资源时,线程D若运行则被挂起,直到W运行完,D继续运行
5.编写代码:
线程函数:
UINT WriteW(LPVOID pParam){
CEdit *pEdit=(CEdit*)pParam;
pEdit->SetWindowText(L"");
critical_section.Lock(); //锁定临界区:其它线程遇到critical_section.Lock();时要等待,直到执行完critical_section.Unlock();
for(int i=0;i<5;i++){
g_Array[i]=L'W';
pEdit->SetWindowText(g_Array);
Sleep(1000);
}
critical_section.Unlock();
return 0;
}
UINT WriteD(LPVOID pParam){
CEdit *pEdit=(CEdit*)pParam;
pEdit->SetWindowText(L"");
critical_section.Lock();
for(int i=0;i<5;i++){
g_Array[i]=L'D';
pEdit->SetWindowText(g_Array);
Sleep(1000);
}
critical_section.Unlock();
return 0;
}
两个按钮的消息函数:
void CMultiThred8Dlg::OnBnClickedWritew(){ // TODO: 在此添加控件通知处理程序代码
CWinThread *pWriteW = AfxBeginThread(
WriteW,
&m_ctrlW,
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED
);
pWriteW->ResumeThread();
}
void CMultiThred8Dlg::OnBnClickedWrited(){ // TODO: 在此添加控件通知处理程序代码
CWinThread *pWriteD = AfxBeginThread(
WriteD,
&m_ctrlD,
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED
);
pWriteD->ResumeThread();
}
MultiThread9
演示CEvent用法。
1.建立一个基于对话框的工程MultiThread9,在对话框中加入一个按钮IDC_WRITEW,标题为“W”,和两个编辑框IDC_W
和IDC_D,属性都选中Read-only;
2.在MultiThread9Dlg.h中添加:
UINT WriteW(LPVOID pParam);
UINT WriteD(LPVOID pParam);
3.给IDC_W和IDC_D添加CEdit变量m_ctrlW和m_ctrlD;
4.在MultiThread9Dlg.cpp中添加:
#include "afxmt.h" //为了能正确使用同步类
CEvent eventWriteD; //为了能在不同线程间使用,定义为全局变量
TCHAR g_Array[5]; //共享资源
5.编写代码:
线程函数:
UINT WriteW(LPVOID pParam){
CEdit *pEdit=(CEdit*)pParam;
pEdit->SetWindowText(L"");
for(int i=0;i<4;i++){
g_Array[i] = L'W';
pEdit->SetWindowText(g_Array);
Sleep(1000);
}
eventWriteD.SetEvent();
return 0;
}
UINT WriteD(LPVOID pParam){
CEdit *pEdit=(CEdit*)pParam;
pEdit->SetWindowText(L"");
WaitForSingleObject(eventWriteD.m_hObject,INFINITE);
for(int i=0;i<4;i++){
g_Array[i] = L'D';
pEdit->SetWindowText(g_Array);
Sleep(1000);
}
return 0;
}
按钮的消息函数:
void CMultiThread9Dlg::OnBnClickedWritew(){
// TODO: 在此添加控件通知处理程序代码
CWinThread *pWriteW = AfxBeginThread(
WriteW,
&m_ctrlW,
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED);
CWinThread *pWriteD = AfxBeginThread(
WriteD,
&m_ctrlD,
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED);
pWriteD->ResumeThread();
/*虽然先运行D,但D中WaitForSingleObject(监视eventWriteD)将D挂起,直到W中SetEvent设置eventWriteD为有信号*/ pWriteW->ResumeThread();
}
MultiThread10
演示CSemaphore用法。
1.建立一个基于对话框的工程MultiThread10,在对话框中加入一个按钮IDC_START以及三个编辑框IDC_A、IDC_B和IDC_C,
其属性都选中Read-only;
2.在MultiThread10Dlg.h中添加:
UINT WriteA(LPVOID pParam);
UINT WriteB(LPVOID pParam);
UINT WriteC(LPVOID pParam);
3.给IDC_A、IDC_B和IDC_C添加CEdit变量m_ctrlA、m_ctrlB和m_ctrlC;
4.在MultiThread10Dlg.cpp中添加:
#include "afxmt.h" //为了能正确使用同步类
CSemaphore semaphoreWrite(2, 2);//最多访问资源的线程2个,当前可访问资源的线程2个TCHAR g_Array[5]; //共享资源
5.编写代码:
线程函数:
UINT WriteA(LPVOID pParam){
CEdit *pEdit=(CEdit*)pParam;
pEdit->SetWindowText(L"");
WaitForSingleObject(semaphoreWrite.m_hObject,INFINITE);
CString str;
for(int i=0;i<4;i++){
pEdit->GetWindowText(str);
g_Array[i]=L'A';
str=str+g_Array[i];
pEdit->SetWindowText(str);
Sleep(1000);
}
ReleaseSemaphore(semaphoreWrite.m_hObject,1,NULL);
return 0;
}
UINT WriteB(LPVOID pParam){
CEdit *pEdit=(CEdit*)pParam;
pEdit->SetWindowText(L"");
WaitForSingleObject(semaphoreWrite.m_hObject,INFINITE);
CString str;
for(int i=0;i<4;i++){
pEdit->GetWindowText(str);
g_Array[i]=L'B';
str=str+g_Array[i];
pEdit->SetWindowText(str);
Sleep(1000);
}
ReleaseSemaphore(semaphoreWrite.m_hObject,1,NULL);
return 0;
}
UINT WriteC(LPVOID pParam){
CEdit *pEdit=(CEdit*)pParam;
pEdit->SetWindowText(L"");
WaitForSingleObject(semaphoreWrite.m_hObject,INFINITE);
for(int i=0;i<4;i++){
g_Array[i]=L'C';
pEdit->SetWindowText(g_Array);
Sleep(1000);
}
ReleaseSemaphore(semaphoreWrite.m_hObject,1,NULL);
return 0;
}
//线程执行到WaitForSingleObject,若信号量对象有信号,则继续执行,同时可用线程数减1;
//线程执行到WaitForSingleObject,若信号量对象无信号,则线程等待,直到信号量对象有信号。
按钮的消息函数:
void CMultiThread10Dlg::OnBnClickedStart(){
CWinThread *pWriteA=AfxBeginThread(
WriteA,
&m_ctrlA,
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED);
pWriteA->ResumeThread();
CWinThread *pWriteB=AfxBeginThread(
WriteB,
&m_ctrlB,
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED);
pWriteB->ResumeThread();
CWinThread *pWriteC=AfxBeginThread(
WriteC,
&m_ctrlC,
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED);
pWriteC->ResumeThread();
}。