MFC多线程编程.docx

上传人:b****8 文档编号:12437546 上传时间:2023-06-05 格式:DOCX 页数:30 大小:26.69KB
下载 相关 举报
MFC多线程编程.docx_第1页
第1页 / 共30页
MFC多线程编程.docx_第2页
第2页 / 共30页
MFC多线程编程.docx_第3页
第3页 / 共30页
MFC多线程编程.docx_第4页
第4页 / 共30页
MFC多线程编程.docx_第5页
第5页 / 共30页
MFC多线程编程.docx_第6页
第6页 / 共30页
MFC多线程编程.docx_第7页
第7页 / 共30页
MFC多线程编程.docx_第8页
第8页 / 共30页
MFC多线程编程.docx_第9页
第9页 / 共30页
MFC多线程编程.docx_第10页
第10页 / 共30页
MFC多线程编程.docx_第11页
第11页 / 共30页
MFC多线程编程.docx_第12页
第12页 / 共30页
MFC多线程编程.docx_第13页
第13页 / 共30页
MFC多线程编程.docx_第14页
第14页 / 共30页
MFC多线程编程.docx_第15页
第15页 / 共30页
MFC多线程编程.docx_第16页
第16页 / 共30页
MFC多线程编程.docx_第17页
第17页 / 共30页
MFC多线程编程.docx_第18页
第18页 / 共30页
MFC多线程编程.docx_第19页
第19页 / 共30页
MFC多线程编程.docx_第20页
第20页 / 共30页
亲,该文档总共30页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

MFC多线程编程.docx

《MFC多线程编程.docx》由会员分享,可在线阅读,更多相关《MFC多线程编程.docx(30页珍藏版)》请在冰点文库上搜索。

MFC多线程编程.docx

MFC多线程编程

MFC多线程编程

1.线程的基本概念

进程和线程都是操作系统的概念。

进程是应用程序的执行实例。

每个进程是由私有的虚拟地址空间、代码、数据和其它系统资源组成的。

进程在运行过程中创建的资源随着进程的终止而被销毁,所使用的系统资源在进程终止时被释放或关闭。

线程是进程内部的一个执行单元。

系统创建进程后,实际上就是启动了该进程的主执行线程,它以函数地址的形式(比如main或WinMain函数),将程序的启动点提供给Windows系统。

主执行线程终止了,进程也就随之终止。

每个进程至少有一个主执行线程,它无需由用户主动创建,而是由系统自动创建的。

用户根据需要在应用程序中创建其它线程,多个线程并发地运行于同一个进程中。

一个进程中的所有线程都在该进程的虚拟地址空间中,共同使用这些虚拟地址空间、全局变量和系统资源,所以线程间的通讯非常方便,多线程技术的应用也较为广泛。

多线程可以实现并行处理,避免了某项任务长时间占用CPU。

单CPU运行多线程时,操作系统为每个线程安排一些CPU时间,轮换方式向线程提供时间片,这就给人一种假象,似乎这些线程在同时运行。

由此可见,如果两个非常活跃的线程为了抢夺CPU的控制权,在线程切换时会消耗很多CPU资源,反而会降低系统性能。

这点要在编程时注意。

创建线程共三种方法:

(1)WIN32API的CreateThread()

(2)MFC全局函数AfxBeginThread()

(3)CWinThread:

:

CreateThread()

2.WIN32API多线程

2.1WIN32API多线程函数

1.HANDLECreateThread(//方法一

LPSECURITY_ATTRIBUTESlpThreadAttributes,

DWORDdwStackSize,

LPTHREAD_START_ROUTINElpStartAddress,

LPVOIDlpParameter,

DWORDdwCreationFlags,

LPDWORDlpThreadId);

2.DWORDSuspendThread(HANDLEhThread);//挂起指定的线程

3.DWORDResumeThread(HANDLEhThread);//结束线程的挂起状态,执行线程

4.VOIDExitThread(DWORDdwExitCode);//主要在线程的执行函数中被调用,终结线程自身的执行

5.BOOLTerminateThread(HANDLEhThread,DWORDdwExitCode);//强行终止线程,一般不建议使用:

(1)是不安全的,可能会引起系统不稳定;

(2)虽然该函数立即终止线程,但并不释放线程所占用的资源。

6.BOOLPostThreadMessage(

DWORDidThread,

UINTMsg,

WPARAMwParam,

LPARAMlParam);//将一条消息放到指定线程的消息队列中,并且不等到消息被该线程处理时便返回。

如果接收消息的线程没有创建消息循环,则该函数执行失败。

7.DWORDWaitForSingleObject(HANDLEhHandle,DWORDdwMilliseconds);//功能:

在某一线程(创建子线程的线程)中调用,线程暂时挂起,系统监视hHandle(子线程句柄)所指向的对象的状态。

返回:

如果在挂起的dwMilliseconds毫秒内,线程所等待的对象变为有信号状态,则该函数立即返回;如果超时时间已经到达dwMilliseconds毫秒,但hHandle所指向的对象还没有变成有信号状态,函数照样返回。

参数dwMilliseconds有两个特殊值:

0和INFINITE。

若为0,立即返回;若为INFINITE,则线程一直被挂起,直到hHandle所指向的对象变为有信号状态。

2.2例程

MultiThread1

使用Win32API编写多线程。

1.建立基于对话框的工程,在对话框中加入两个按钮和一个编辑框,两个按钮的ID分别是IDC_START、IDC_STOP,标题分别为“启动”,“停止”,IDC_STOP的属性选中Disabled;编辑框IDC_TIME的属性选中Readonly;

2.在MultiThread1Dlg.h中添加线程函数声明voidThreadFunc();(应在类CMultiThread1Dlg声明外部)

3.在类CMultiThread1Dlg内部添加protected型变量:

HANDLEhThread;//线程句柄

DWORDThreadID;//线程ID

4.在MultiThread1Dlg.cpp文件中添加全局变量m_bRun:

volatileBOOLm_bRun;//代表线程是否正在运行

volatile修饰符:

告诉编译器无需对该变量作任何的优化,即无需将它放到一个寄存器中,并且该值可被外部改变。

对于多线程引用的全局变量来说,volatile是一个非常重要的修饰符;

5.编写代码:

IDC_START的消息函数:

voidCMultiThread1Dlg:

:

OnStart(){

//TODO:

Addyourcontrolnotificationhandlercodehere

hThread=CreateThread(

NULL,

0,

(LPTHREAD_START_ROUTINE)ThreadFunc,

NULL,

0,

&ThreadID);

GetDlgItem(IDC_START)->EnableWindow(FALSE);

GetDlgItem(IDC_STOP)->EnableWindow(TRUE);

}

IDC_STOP的消息函数:

voidCMultiThread1Dlg:

:

OnStop(){

//TODO:

Addyourcontrolnotificationhandlercodehere

m_bRun=FALSE;

GetDlgItem(IDC_START)->EnableWindow(TRUE);

GetDlgItem(IDC_STOP)->EnableWindow(FALSE);

}

线程函数:

voidThreadFunc(){

CTimetime;

CStringstrTime;

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中添加线程函数声明:

voidThreadFunc(intinteger);(在类CMultiThread2Dlg声明外部)

3.在类CMultiThread2Dlg内部添加protected型变量:

HANDLEhThread;//线程句柄

DWORDThreadID;//线程ID

4.利用ClassWizard为编辑框IDC_COUNT添加intm_nCount;

5.编写代码:

IDC_START的消息函数:

voidCMultiThread2Dlg:

:

OnStart(){

UpdateData(TRUE);

intinteger=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);

}

线程函数:

voidThreadFunc(intinteger){

inti;

for(i=0;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声明外部)

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

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 法律文书 > 调解书

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2