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声明外部)
structthreadInfo{
UINTnMilliSecond;
CProgressCtrl*pctrlProgress;
};
4.在MultiThread3Dlg.h文件中添加线程函数声明:
UINTThreadFunc(LPVOIDlpParam);(在类CMultiThread3Dlg声明外部)
5.在类CMultiThread3Dlg内部添加protected型变量:
HANDLEhThread;
DWORDThreadID;
6.在MultiThread3Dlg.cpp文件中定义公共变量threadInfoInfo;
7.编写代码:
IDC_START的消息函数:
voidCMultiThread3Dlg:
:
OnStart(){
//TODO:
Addyourcontrolnotificationhandlercodehere
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正在设置进度条,它等主线程将进度条刷新的消息处理完后才会检测通知事件,这样两个线程都在互相等待,死锁发生了,编程时应注意避免。
*/
}
在BOOLCMultiThread3Dlg:
:
OnInitDialog()中添加代码:
{
……
//TODO:
Addextrainitializationhere
m_ctrlProgress.SetRange(0,99);
m_nMilliSecond=10;
UpdateData(FALSE);
returnTRUE;//returnTRUEunlessyousetthefocustoacontrol
}
线程函数:
UINTThreadFunc(LPVOIDlpParam){
threadInfo*pInfo=(threadInfo*)lpParam;
for(inti=0;i<100;i++){
intnTemp=pInfo->nMilliSecond;
pInfo->pctrlProgress->SetPos(i);
Sleep(nTemp);
}
return0;
}
MultiThread4
测试在Windows下最多可创建线程的数目。
1.建立一个基于对话框的工程,在对话框中加入一个按钮IDC_TEST,标题为“测试”,一个编辑框IDC_COUNT,属性选中Read-only;
2.在MultiThread4Dlg.cpp中添加公共变量volatileBOOLm_bRunFlag=TRUE;//表示是否还能继续创建线程
3.为IDC_COUNT添加intm_nCount;
4.在MultiThread4Dlg.h中添加:
DWORDWINAPIthreadFunc(LPVOIDthreadNum);(在类CMultiThread4Dlg外部)
5.编写代码:
IDC_TEST的消息函数:
voidCMultiThread4Dlg:
:
OnTest(){
DWORDthreadID;
GetDlgItem(IDC_TEST)->EnableWindow(FALSE);
longnCount=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;
}
线程函数:
DWORDWINAPIthreadFunc(LPVOIDthreadNum){
while(m_bRunFlag){
Sleep(3000);
}
return0;
}
3.MFC多线程
3.1基本概念
MFC中有两类线程:
(1)工作者线程;
(2)用户界面线程。
二者主要区别:
(1)没有消息循环,
(2)有自己的消息队列和消息循环。
(1)通常用来执行后台计算和维护任务,如冗长的计算,后台打印机打印等。
(2)一般用于处理独立于其他线程之外的用户输入,响应用户及系统所产生的事件和消息等。
3.1.1MFC中创建线程
1MFC全局函数AfxBeginThread():
它有两种重载形式,分别用于创建工作者线程和用户界面线程:
//方法二
(1)CWinThread*AfxBeginThread(
AFX_THREADPROCpfnThreadProc,//工作者线程函数指针UINTExecutingFunction(LPVOIDpParam);其返回值为0
LPVOIDpParam,
nPriority=THREAD_PRIORITY_NORMAL,
UINTnStackSize=0,
DWORDdwCreateFlags=0,
LPSECURITY_ATTRIBUTESlpSecurityAttrs=NULL);
(2)CWinThread*AfxBeginThread(
CRuntimeClass*pThreadClass,
intnPriority=THREAD_PRIORITY_NORMAL,
UINTnStackSize=0,
DWORDdwCreateFlags=0,
LPSECURITY_ATTRIBUTESlpSecurityAttrs=NULL);
2CWinThread类:
(1)创建CWinThread类的一个对象,
(2)调用CWinThread:
:
CreateThread()启动线程:
//方法三
(1)BOOLCWinThread:
:
CreateThread(
DWORDdwCreateFlags=0,
UINTnStackSize=0,
LPSECURITY_ATTRIBUTESlpSecurityAttrs=NULL);
(2)virtualBOOLCWinThread:
:
InitInstance();//用于用户界面线程的初始化;工作者线程一般不用
(3)virtualintCWinThread:
:
ExitInstance();//线程终结前进行一些清理工作;通常用于用户界面线程
CWinThread类的用法:
A.创建用户界面线程的步骤:
1.创建类CWinThread的派生类(以CUIThread类为例)
2.重载InitInstance()与ExitInstance():
BOOLCUIThread:
:
InitInstance(){
CFrameWnd*wnd=newCFrameWnd;
wnd->Create(NULL,"UIThreadWindow");
wnd->ShowWindow(SW_SHOW);
wnd->UpdateWindow();
m_pMainWnd=wnd;
returnTRUE;
}
3.创建用户界面线程:
voidCUIThreadDlg:
:
OnButton1(){
CUIThread*pThread=newCUIThread();
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(
BOOLbInitiallyOwn=FALSE,//指定事件对象初始化状态:
TRUE为有信号
BOOLbManualReset=FALSE,//指定要创建的是人工事件还是自动事件:
TRUE为人工事件
LPCTSTRlpszName=NULL,//一般为NULL
LPSECURITY_ATTRIBUTESlpsaAttribute=NULL);//一般为NULL
2.BOOLCEvent:
:
SetEvent();
//对于人工事件,它将CEvent对象保持为有信号,直到调用ResetEvent()为止;
//对于自动事件,它将CEvent对象设置为有信号,CEvent对象由系统自动重置为无信号。
3.BOOLCEvent:
:
ResetEvent();
//它将事件设置为无信号,并保持该状态直至调用SetEvent()为止。
//自动事件不需要调用它。
//一般通过WaitForSingleObject()监视事件状态。
3.1.3.4CSemaphore
CSemaphore提供了访问共享资源线程的计数,它能限制访问共享资源线程的数目。
CSemaphore构造函数:
CSemaphore(
LONG