实验9 多线程程序设计.docx
《实验9 多线程程序设计.docx》由会员分享,可在线阅读,更多相关《实验9 多线程程序设计.docx(12页珍藏版)》请在冰豆网上搜索。
实验9多线程程序设计
实验九多线程程序设计
一、实验目的
1、工作线程的创建;
2、多线程的创建;
3、主程序与线程间数据交换;
4、线程工作状态设置及修改。
二、实验要求
2.1创建一个窗口,建立子菜单(创建线程(BEGIN_THREAD)、线程关闭(CLOSE)),利用创建线程BEGIN_THREAD,创建许多工作线程,利用线程关闭(CLOSE)所有线程。
每个工作线程都对全局变量intgl_i加100,然后在线程中判别全局变量BOOLgl_bclose,当判别为线程结束时,将当前线程获得的gl_i在对话中显示(即中间对话中显示的值)。
注意,由于线程是一个简单的延时后累加的过程,所有随着控制线程是否结束的时间不同,得到的值是不相同的。
控制线程结束,利用线程关闭(CLOSE)实现对gl_bclose的修改。
2.2同1,要求利用子菜单选择同时启动两个线程,在这两个线程中分别有各自的累加值并在线程中对其进行累加,不过要求两个线程有不同的优先级别,当线程关闭后,显示两线程当前的累计值。
2.3编写一个线程,功能是将主程序通过利用参数传递方法传递给线程的一个整型数(或一个类)定时累加。
建立一个对话窗口,有线程的‘启动’、‘挂起’/‘运行’和‘停止’三个按钮,‘启动按钮’实现启动一个线程,‘挂起’/‘运行’是一个按钮,是根据当前线程的状态设置,显然当线程在‘运行’状况,该按钮应该显示‘挂起’,反之,显示‘运行’,当按该按钮后,使线程处于挂起状态或运行状态,‘停止’按钮显然是停止该线程的运行。
另有一个‘MOVE’按钮和一个编辑控件,按‘MOVE’按钮将当前线程中的整型值在编辑控件上显示。
目的:
了解线程与主程序间的参数传递。
三、实验过程
3.1首先创建单文档工程,如下图:
3.2第一部分──线程的创建与结束:
3.2.1添加全局变量:
intgl_i;//计数
BOOLgl_bclose;
//线程控制:
true-结束所有线程;false-线程继续执行
3.2.2线程的创建:
线程靠一个CWinThread类的对象来管理,启动一个新线程的方法很简单,用到API函数的AfxBeginThread,定义如下:
CWinThread*AFXAPIAfxBeginThread
(AFX_THREADPROCpfnThreadProc,//线程函数的地址
LPVOIDpParam, //传送到线程函数的参数
intnPriority, // (可选的)线程的优先级,默认的是平常的优先级,
UINTnStackSize, // (可选的)线程的堆栈大小
DWORDdwCreateFlags,// (可选的)如果用户创建的线程在开始的时候在挂起态,而不在运行态,可以设置为CREATE_SUSPENDED
LPSECURITY_ATTRIBUTESlpSecurityAttrs
//(可选的)线程的安全属性,默认的是和父线程的访问权限一样
);
函数AfxBeginThread启动新线程并返回一个线程对象指针,然后,新线程和启动新线程的线程同时运行。
函数的第一个参数指定线程函数。
新线程首先执行这个函数,在这个函数中可以再调用其他函数完成新线程的工作,这个函数执行完毕返回时,新线程结束。
线程函数不能是任意定义,必须定义如下:
UINTThreadFunction(LPVOIDpParam);
这个函数必须被定义成全局函数,函数的返回类型必须是UINT,必须有一个LPVOID类型的参数,这个参数一般使用AfxBeginThread函数的第二个参数pParam。
函数的返回值在函数中任意定义(但不能返回特殊值STILL_AVTIVE,它被定义成0X00000l03L,表示线程仍在运行,引起混淆)。
AfxBeginThread函数的具体用法可查阅MSDN。
3.2.3线程函数:
线程函数定义了线程要做什么,在进入这个函数的时候线程开始,退出的时候线程结束。
这个函数称为线程函数,定义如下:
UINTThreadFunction(LPVOIDpParam);
这里:
ThreadFunction是线程函数名,就是启动线程函数的第一个参数pfnThreadProc。
参数LPVOIDpParam是启动线程函数的第二个参数,该参数是一个32位数,是在线程对象创建时传送给对象的构造函数。
线程函数根据需要进行对参数进行处理,实际上,线程和主程序之间可以利用这个参数实现数据传递。
3.2.4终止线程:
线程终止由两种方法,一种是等待线程函数执行完毕后自动退出,线程退出后创建的线程对象是否自动删除,是由CWinThread对象的一个成员变量m_bAutoDelete决定,为TRUE时,线程执行后自动删除创建的CWinThread对象,否则,在线程结束后,该对象并不删除,需要外部来删除。
可以在线程创建后对m_bAutoDelete设置。
如:
m_pDrawThread->m_bAutoDelete=FALSE;
这样,在线程执行结束后并不删除,需要外部去删除:
deletem_pDrawThread;
另一个终止线程的方法是调用MFC函数AfxEndThread:
voidAFXAPIAfxEndThread(UINTnExitCode,BOOLbDelete);
延时计数可利用函数:
VOIDSleep(
DWORDdwMilliseconds//延时长度(单位:
毫秒)
);
实验九第一部分最终结果如下图:
第一部分程序如下:
classCtest9//定义一个类
{
public:
Ctest9(){m_i=0;};
voidSet(inti){m_i=i;};
voidSetId(inti){id=i;};
intGet(){returnm_i;};
intGetId(){returnid;};
voidInc(){m_i+=1;};
private:
intm_i;//累计值
intid;//线程编号};
UINTtestThread1(LPVOIDpParam){//线程主函数
//接收一个窗口类指针,然后设置窗口标题
HWNDhWnd=(HWND)pParam;//将传入的线程函数参数转换为窗口句柄
while(!
gl_bclose){//判别gl_bclose是否为TRUE,否则线程继续运行
Sleep(1000);//1s
gl_i+=100;//延时累加
}
charstr[81];
wsprintf(str,"全局变量值:
%d",gl_i);
AfxMessageBox(str);//显示gl_i值
return0;//返回并退出线程
//或者调用voidAfxEndThread(UINTnExitCode);来退出
}
voidOnThreadCreate()
{
gl_bclose=FALSE;
HWNDhWnd=GetSafeHwnd();
AfxBeginThread(testThread1,hWnd);//创建工作线程
}
3.3第二部分──创建优先级不同的双线程:
3.3.1创建双线程:
虽然是创建双线程,但其实两个线程的内部工作方式相同,只是传递的参数不同,因此只需一个线程函数即可:
UINTtestDoubleThread(LPVOIDpParam);
然后利用3.2.2中提到的AfxBeginThread()来创建线程:
AfxBeginThread(testDoubleThread,pParam1,THREAD_PRIORITY_ABOVE_NORMAL);
//创建第一个线程,优先级高于默认
AfxBeginThread(testDoubleThread,pParam2,THREAD_PRIORITY_BELOW_NORMAL);
//创建第二个线程,优先级低于默认
3.3.2终止双线程:
同样可以利用3.2中的全局变量gl_bclose来控制线程的结束。
最终结果如下图:
第二部分程序如下:
UINTtestDoubleThread(LPVOIDpParam){//线程主函数
Ctest9*pCtest9;
pCtest9=(Ctest9*)pParam;
while(!
gl_bclose)
{
Sleep(500);
pCtest9->Inc();
}
charstr[81];
wsprintf(str,"线程%d累计值:
%d",pCtest9->GetId(),pCtest9->Get());
AfxMessageBox(str);
if(pCtest9!
=NULL){deletepCtest9;}
return0;
}
voidCMainFrame:
:
OnDoublethreadCreate()
{
gl_bclose=FALSE;
Ctest9*pCtest9_1;
pCtest9_1=newCtest9;
pCtest9_1->SetId
(1);
LPVOIDpParam1=(LPVOID)pCtest9_1;
AfxBeginThread(testDoubleThread,pParam1,THREAD_PRIORITY_ABOVE_NORMAL);
//创建第一个线程,优先级高于默认
Ctest9*pCtest9_2;
pCtest9_2=newCtest9;
pCtest9_2->SetId
(2);
LPVOIDpParam2=(LPVOID)pCtest9_2;
AfxBeginThread(testDoubleThread,pParam2,THREAD_PRIORITY_BELOW_NORMAL);
//创建第二个线程,优先级低于默认
}
3.4第三部分──线程的控制:
3.4.1插入对话框,如下图:
3.4.1为工程添加变量:
为3个EditBox分别添加成员变量:
intm_Value1;//计数初始值
intm_Value2;//计数增量
intm_Value3;//计数结果
四个按钮分别用来创建线程,暂停/运行,终止线程,显示最终结果。
按钮的状态可由下面两个函数控制:
CButton:
:
EnableWindow(BOOLbEnable);
//TRUE——可用;FALSE——不可用(变灰)
CButton:
:
SetWindowText(LPCTSTRpszString);
//实现将按钮上的字符改变
添加全局变量:
boolpause_flag=FALSE;//线程结束标志
boolend_flag=FALSE;//线程挂起标志
intgl_last=0;//全局变量,存放累加值
最终结果如下图:
图1:
创建线程
图2:
线程挂起
图3:
线程结束并显示结果
第三部分程序如下:
boolpause_flag=FALSE;//线程结束标志
boolend_flag=FALSE;//线程挂起标志
intgl_last=0;//全局变量,存放累加值
classCTest9_3
{
public:
CTest9_3(){m_InitValue=m_Step=0;}
voidInc(){m_InitValue+=m_Step;}
intGetValue(){returnm_InitValue;}
voidSetValue(intv1,intv2){m_InitValue=v1;m_Step=v2;}
protected:
private:
intm_InitValue;
intm_Step;
};//主程序与线程传递的参数
UINTtestThread3(LPVOIDpParam){
CTest9_3*lpc=(CTest9_3*)pParam;
while(!
end_flag)//线程是否结束?
{
while(pause_flag)//线程是否挂起?
{
Sleep(50);//线程休眠
}
Sleep(1000);
lpc->Inc();//延时累加,1s
}
gl_last=lpc->GetValue();//线程结束,保存累加值
if(lpc!
=NULL){deletelpc;}
return0;
}
voidOnThreadBegin()
{
end_flag=FALSE;
pause_flag=FALSE;
gl_last=0;//初始化
UpdateData(TRUE);//获取输入的初始值以及步长值
CTest9_3*lpc;
lpc=newCTest9_3;
lpc->SetValue(m_InitValue,m_Step);
LPVOIDpParam=(LPVOID)lpc;//定义线程操作的数据
CButton*button=(CButton*)GetDlgItem(IDC_THREAD_BEGIN);
button->EnableWindow(FALSE);//“启动”按钮失效(变灰)
AfxBeginThread(testThread3,pParam);//启动线程
}
voidOnThreadRunpause()
{
CButton*button=(CButton*)GetDlgItem(IDC_THREAD_RUNPAUSE);
if(!
pause_flag)
{
pause_flag=TRUE;
button->SetWindowText("运行");//线程运行时显示“挂起”
}
else
{
pause_flag=FALSE;
button->SetWindowText("挂起");//线程挂起时显示“运行”
}
}
voidOnThreadEnd()
{
end_flag=TRUE;//通知线程结束
CButton*button=(CButton*)GetDlgItem(IDC_THREAD_BEGIN);
button->EnableWindow(TRUE);//使“启动”按钮恢复作用
}
voidOnValueMove()
{
//TODO:
Addyourcontrolnotificationhandlercodehere
m_LastValue=gl_last;
UpdateData(FALSE);//将累加值传递给EditBox变量并在EditBox中显示
}