ghost win7旗舰版-空调怎么制热
![duplicatehandle](/uploads/image/0322.jpg)
2023年4月3日发(作者:手机http代理怎么设置)
创建线程,利用互斥实现线程共享变量通信
一.概述
1.1课题目的和意义
掌握线程创建和终止,加深对线程和进程概念的理解,会用同步与互斥方法实现线程之间的
通信。
1.2内容和要求
软件界面上点“创建线程”按钮,创建三个生产者线程(P1,P2,P3)和两个消费者线程(C1,C
2),生产者和消费者线程共享一个长度为2KB的环型公共缓冲区,生产者向其中投放消息,消费
者从中取走消息。只要缓冲区未满,生产者可将消息送入缓冲区;只要缓冲区未空,消费者可从
缓冲区取走一个消息。
每个消息具下列结构格式:
消息头(1B,固定为0xaa),消息长度(1B),消息内容(nB),校验和(1B),检验和计算方式为
消息长度和消息内容所有字节异或结果。
每个生产者每隔n毫秒(n用随机数产生,1到100毫秒之间,间隔不固定)生产一个消息加
入缓冲区,并把消息产生时间和内容记录在一个文本文件中(或显示在列表框中)。P1每次生产
的数据为26个大写字母,P2每次生产的数据为26个小写字母,P3每次生产的数据为10个数
字。
每个消费者每隔n秒(n用随机数产生,1到5秒之间,间隔不固定)从缓冲区取走一个消息。
每消费一个消息需要将消费时间和消息内容记录在一个文本文件中(或显示在列表框中)。
当用户按结束按钮时结束5个线程,并将5个文件内容显示出来进行对照。
这期实是一个经典的生产者—消费者(Producer_consumer)进程(线程)同步的问题。它描
述的是:有一群生产者进程在生产产品,并将此产品提供给消费者进程(线程)去消费。为使生产
者进程和消费者进程(线程)能并发执行,在它们之间设置有个缓冲区的缓冲池,生产者进程(线程)
可将它所生产的产品放入一个缓冲区中,消费者进程(线程)可从一个缓冲区取得一个产品消费。
尽管所有的生产者进程和消费者进程(线程)都是以异步的方式运行的,但它们之间必须保持同步,
即不允许消费者进程(线程)到一个空缓冲区去取产品,也不允许生产者进程(线程)向一个已装有
消息尚未被取走产品的缓冲区投放产品。如下图所示:
1.3线程所采用的同步方法
同步是多线程中的重要概念.同步的使用可以保证在多线程运行的环境中,程充不会产生设
计之外的结果.同步的实现方式有两种,同步方法和同步块.
线程在执行同步方法是具有排它性的.当任意一个线和进入到一个对象的任意一个同步
方法时,这个对象所有同步方法都被锁定,在些期间,期他任何线程都不能访问这个对象的任意一
个同步方法,直到这个线程执行完它所调用的同步方法并从中退出,从而导至它释放了该对象
的同步锁这后.在一个对象被某个线程锁定之后,其他线程是可以访问.
同步的有几种实现方法,分别是:
wait():使一个线程处于等待状态,并且释放所有持有的对象lock.
sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉Inter
ruptedException异常。
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的
唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,
而是让它们竞争。
1.4开发工具平台
开发平台:windowXP开发工具:VC++
二.数据定义和详细说明
1数据定义
设计PV操作算法,用信号量机制实现生产者与消费者同步与互斥问题,并与无PV情况下进
行对比。定义20个缓冲区,将其初始化为0。调用随机函数rand()生成随机数,把随机数通过
生产者放入缓冲区。若缓冲区满则其值非0。当消费者从缓冲区中去数后缓冲区值变为0。程序
可显示缓冲区中的全部内容,方便观察生产者与消费者的行为。程序可通过设置sleep(time)
中time的值来控制生产者与消费者的执行顺序。
2详细说明
为了实现生产者与消费者同步与互斥的问题,该程序用记录型信号量机制来实现。
假定在生产者与消费者之间,利用一个公共的缓冲池来进行通信,生产者将所生产的信息
放入其中,消费者cognitive缓冲池中取得消息来消费,该缓冲池具有n个缓冲区,其编号为0,
1,2,3···,n-1;设置一个互斥信号量mutex,用于实现诸进程对缓冲池的互斥使用,其初值
为1,利用资源信号量empty,表示缓冲池中空缓冲区的数目,其初值为n;full分别表示缓冲池中满
缓冲区的数目,其初值为0.又假定这些生产者和消费者相互等效,只要缓冲池未满,生产者便可将
消息送入由指针in所指的缓冲区;只要缓冲池未空,消费者变可以从由指针out所指示的缓冲
区中,取走一个消息。对生产者消费者的问题可以描述如下:
Varmetux,empty,full:semaphore=1,n,0;
Buffer:array[0,···n-1]ofitem;
In,out:integer:=0;
Begin:
Pabegin
Producer:begin
Repeat
Produceranitemnextp;
P(empty);
P(mutex);
buffer(in):=nextp;
in:=(in+1)modn;
V(mutex);
V(full);
Untilfalse;
End
Consumer:begin
Repeat
P(full);
P(mutex);
nextc:=buffer(out);
out:=(out+1)modn;
V(mutex);
V(empty);
Untilfalse;
End
Parend
End
ﻬ
三.实现思想和设计流程
1实现思想
我们把系统中使用某一类资源的进程(线程)称为该资源的消费者,而把释放同类资源的进程
称为该资源的生产者。例如在计算进程(线程)与打印进程(线程)公用一个缓冲区时,计算进程(线
程)把数据送入缓冲区,打印进程(线程)从缓冲区中取数据打印输出,因此,计算进程相当于数据资
源的生产者,而打印进程相当于消费者,二者之间必须保持同步。基于这一问题,我们将使用
生产者和消费者这一同步机制算法来处理该问题.
2设计流程
首先,我们知道,生产者—消费者问题是一个同步问题。即生产者和消费者之间应满足如下
条件:
2.1消费者想接收数据时,有界缓冲区中至少有一个单元是满的。
2.2生产者想发送数据时,有界缓冲区中至少有一个单元是空的。
另外,由于有界缓冲区是临界资源,因此,各生产者进程和各消费者进程之间必须互斥。其次,
我们还必须考虑面临的问题是属于进程互斥还是进程同步,或是互斥与同步的混合问题。然后
根据共享资源的数量以及使用共享资源的规则正确的定义信号量及其初值。
最后,还要对结果进行分析处理。若结果中生产和消费进程都已处理完时,但还可能出现
以下两种情况:一是还有生产进程,但没有空缓冲,且消费进程暂时已完,所以此时,只能结束等待
新的消费进程产生空缓冲。二是还有消费进程,但没有了满缓冲,且生产进程暂时已完,此时,只能
结束等待新的生产进程来输入数据,产生新的满缓冲等。在程序中应能作出相应的判断和处理。
本程序的执行是在C++的环境中通过手动输入生产者和消费者线程的运行速度来控制程
序的运行的。为了实现生产者进程能把生产出来的产品正确的存入缓冲区,和消费者进程能够从
缓冲区中取产品进行消费,防止因等待资源而出现死锁的现象,首先设置两个时间:生产者生产一
个产品后等待的时间t1,和消费者消费一个产品后等待的时间t2,来控制生产者和消费者进程
执行的速度。其函
数原形是sleep(t1)和sleep(t2)其中t1、t2指定义挂起执行线程的时间,以毫秒为
单位,取值为0时,该线程将余下的时间片交给处于就绪状态的同一优先级的其他线程。若没有
处于就绪状态的同一优先级的其他线程,则函数立即返回。
3程序流程图
消费者
有消费请求?
此请求可满足?
确定产品位置
此产品正被消费?
生产者
阻塞
阻塞
存在空缓冲区
另一生产者正
在写?
进入临界区
阻塞
阻塞
N
Y
N
Y
创建进程模拟生产者消费者
图3-1程序流程图
4关键代码分析
本程序采用了MFC可视化界面来完成,现在给出关键代码来分析.
4.1开始创建线程
原代码:
voidCMultiThreadDlg::OnStart()//开始创建线程
{
hMutex=CreateMutex(NULL,FALSE,NULL);//创建互斥对象
threadController=1;
ﻩcheck=TRUE;//检测标识
ﻩHWNDhWnd=GetSafeHwnd();//得到控制权
AfxBeginThread(ThreadProc,hWnd,THREAD_PRIORITY_NORMAL);//启用
生产者线程1(P1)
ﻩAfxBeginThread(ThreadProc2,hWnd,THREAD_PRIORITY_NORMAL);//启用生产者
线程2(P2)
AfxBeginThread(ThreadProc3,hWnd,THREAD_PRIORITY_NORMAL);//
启用生产者线程3(P3)
AfxBeginThread(Thread_consumer,hWnd,THREAD_PRIORITY_NORMAL);
//启用消费者线程1(S1)
ﻩAfxBeginThread(Thread_consumer2,hWnd,THREAD_PRIORITY_NORMAL);/
/启用消费者线程2(S2)
}
原码功能:主要创建生产者和消费者线程,创建互斥对象,创建窗体对象.
退出临界区
从空缓冲区中为本生产者分
配一个空间
退出临界区
在该缓冲区中写入产品
通过信号量通知等待本产品
的消费者
结束生产者进程
主要函数功能:
4.1.2CreateMutex()
函数功能:该函数是创建有名或者无名的互斥对象。
函数原型:
HANDLECreateMutex(LPSECURITY_ATTRIBUTESlpMutexAttributes,
BOOLbInitialOwner,LPCTSTRlpName);
参数:lpMutexAttributes:指向SECURITY_ATTRIBUTES结构的指针,该结构决定子
进程是否能继承返回句柄。如果lpMutexAttributes为NULL,那么句柄不能被继承。
在WindowsNT中该结构的lpSecurityDescriptor成员指定新互斥对象的安全描
述符。如果lpMutexAttributes为NULL,那么互斥对象获得缺省的安全描述符。
bInitialOwner:指定互斥对象的初始所属身份。如果该值为TRUE并且调用者创建互
斥对象,那么调用线程获得互斥对象所属身份。否则,调用线程不能获得互斥对象所属身份。判
断调用者是否创建互斥对象请参阅返回值部分。
lpName:指向以NULL结尾的字符串,该字符串指定了互斥对象名。该名字的长度小于
MAX_PATH且可以包含除反斜线路径分隔符()以外的任何字符。名字是区分大小写的。
如果与已存在的有名互斥对象名相匹配,那么该函数要求用__权限访问已存在的对象。在
这种情况下,由于参数己被创建进程所设置,该参数被忽略。如果参数不为,它决定句柄是否
解除继承,但是其安全描述符成员被忽略。
如果lpName为NULL,那么创建的互斥对象无名。
如果lpName与已存在的事件、信号量、可等待定时器、作业、或者文件映射对象的名字
相匹配,那么函数调用失败,并且GetLastError函数返回ERPOR_INVALID
_HANDLE。其原因是这些对象共享相同的名字空间。
返回值:如果函数调用成功,返回值是互斥对象句柄;如果函数调用之前,有名互斥对象已存
在,那么函数给已存在的对象返回一个句柄,并且函数GetLastError返回ERROR_A
LREADY_EXISTS,否则,调用者创建互斥对象。
如果函数调用失败,则返回值为NULL。若想获得更多错误信息,请调用GetLastError
函数。
如果CreateMutex中的lpMutexAttributes参数允许继承,由CreateProcess函
数创建的子进程可以继承父进程的互斥对象句柄。
一个进程可以在调用DuplicateHandle函数时指定互斥对象句柄来创建一个可以被其他
进程使用的双重句柄。一个进程在调用OpenMutex或CreateMutex函数时能指定互斥对象
名。
使用CloseHandle函数关闭句柄,进程结束时系统自动关闭句柄。当最后一
个句柄被关闭时,互斥对象被销毁。
4.1.3GetSafeHwnd()
功能:到一个窗口对象(CWnd的派生对象)指针的句柄(HWND)
函数原型:HWNDhwnd=pwnd->GetSafeHwnd();
函数用法:
CWnd*pwnd=FindWindow(“ExploreWClass”,NULL);//希望找到资源管理器
HWNDhwnd=pwnd->m_hwnd;//得到它的HWND
这样的代码当开始得到的pwnd为空的时候就会出现一个“Generalprotectionerror”,并关闭
应用程序,因为一般不能对一个NULL指针访问其成员,如果用下面的代码:
CWnd*pwnd=FindWindow(“ExploreWClass”,NULL);//希望找到资源管理器
HWNDhwnd=pwnd->GetSafeHwnd();//得到它的HWND
就不会出现问题,因为尽管当pwnd是NULL时,GetSafeHwnd仍然可以用,只是返回NU
LL,通过GetSafeHwnd()的实现代码就更清楚了:
_AFXWIN_INLINEHWNDCWnd::GetSafeHwnd()const
{
returnthis==NULL?NULL:m_hWnd;
}
4.1.4AfxBeginThread()
功能:用于创建工作者线程
函数原型:CWinThread*AfxBeginThread(AFX_THREADPROCpfnThreadProc,LP
VOIDpParam,intnPriority=THREAD_PRIORITY_NORMAL,UINTnStackSize=0,
DWORDdwCreateFlags=0,LPSECURITY_ATTRIBUTESlpSecurityAttrs=NULL);
返回值:一个指向新线程的线程对象.
pfnThreadProc:线程的入口函数,声明一定要如下:UINTMyThreadFunction(
LPVOIDpParam);
pParam:传递入线程的参数,注意它的类型为:LPVOID,所以我们可以传递一个结构体入线
程.
nPriority:线程的优先级,一般设置为0.让它和主线程具有共同的优先级.
nStackSize:指定新创建的线程的栈的大小.如果为0,新创建的线程具有和主线程一样的大
小的栈
dwCreateFlags:指定创建线程以后,线程有怎么样的标志.可以指定两个值:
CREATE_SUSPENDED:线程创建以后,会处于挂起状态,直到调用:ResumeThread0:
创建线程后就开始运行.
lpSecurityAttrs:指向一个SECURITY_ATTRIBUTES的结构体,用它来标志新创建线程的安
全性.如果为NULL,那么新创建的线程就具有和主线程一样的安全性.如果要在线程内结束线
程,可以在线程内调用AfxEndThread.
4.2生产者线程
原码:
UINTCMultiThreadDlg::ThreadProc(LPVOIDparam)//生产者1的生产过程
{
while(threadController)
{
ﻩWaitForSingleObject(hMutex,INFINITE);//等待一个同步事件的到来
ﻩﻩif(number<20)
ﻩ{
ﻩﻩﻩsrand((unsigned)time(NULL));//返回一个随机数
ﻩﻩintn;
ﻩn=rand()%9+1;//随机显示字数
ﻩﻩSleep(n*100);
ﻩﻩCStringstr;
ﻩﻩﻩCStringstr_;
ﻩﻩfor(inti=0;i<n;i++)//循环随机的字数
ﻩ{
ﻩﻩﻩstr_.Format("%c",65+rand()%9+1);//显示大写字母
ﻩﻩstr.Insert(i,str_);//插入成一列
ﻩ}
ﻩﻩﻩlist.AddTail(CString(str));
ﻩﻩﻩCStringstring;
strinmat("%s%d%s%d","0xaa",n,list.GetTail(),sizeof(il())
^sizeof(n));//按格式消息头(1B,固定为0xaa),消息长度(1B),消息内容(nB),校验和
(1B)
pbox1->AddString(string);//打印
ﻩﻩnumber++;
ﻩﻩ}
ﻩReleaseMutex(hMutex);//释放互斥ﻩ
ﻩ}
}
原码功能:当生产者线程1得到控制权,等待同步事件,产生随机个数(n),按产生的随机数显示n
个大写字母,利用sleep函数来隔时存储到缓冲区,最后按格式消息头(1B,固定为0xaa),消息
长度(1B),消息内容(nB),校验和(1B)的方式输出.
主要函数功能:
4.2.1WaitForSingleObject()
函数功能:当如下情况之一发生时该函数返回:指定对象处于信号态;超时。
函数原型:
DWORDWaitForSingleObject(HANDLEhHandle,DWORDdwMillisecond
s);
参数:hHandle:等待对象句柄。若想了解指定句柄的对象类型列表,参阅下面说明部分。
在WndowsNT中,句柄必须有SYNCHRONIZE访问权限。若想获得更多的信息,请查看
StandardAccessRights。
dwMilliseconds:指定以毫秒为单位的超时间隔。如果超时,即使对象的状态是非信号态
的并且没有完成,函数也返回。如果dwMilliseconds是0,函数测试对象的状态并立刻返回:
如果dwMillseconds是INFINlTE,函数从不超时。
返回值:如果函数调用成功,返回值表明引起函数返回的事件。可能值如下:
WAIT_ABANDONED:指定对象是互斥对象,在线程被终止前,线程没有释放互斥对象。
互斥对象的所属关系被授予调用线程,并且该互斥对象被置为非信号态。
WAIT_OBJECT_0:指定对象的状态被置为信号态。
WAlT_TIMEOUT:超时,并且对象的状态为非信号态。
如果函数调用失败,返回值是WAIT_FAILED。若想获得更多错误信息,请调用GetLa
stError函数。
4.2.2ReleaseMutex()
函数功能:该函数放弃指定互斥对象的拥有权。
函数原型:BOOLReleaseMutex(HANDLEhMutex);
参数:hMutex:互斥对象句柄。为CreateMutex或OpenMutex函数的返回值。
返回值:如果函数调用成功,那么返回值是非零值;如果函数调用失败,那么返回值是零值。
若想获得更多错误信息,请调用GetLastError函数。
备注:如果调用线程不拥有互斥对象,ReleaseMutex函数失败。
一个线程通过调用等待函数拥有互斥对象。创建该互斥对象的线程也拥有互斥对象,而不
需要调用等待函数。当互斥对象的所有者线程不再需要互斥对象时,它可以调用ReleaseMutex函
数。
当一个线程拥有一个互斥对象后,它可以用该互斥对象多次调用等待函数而不会阻塞。这
防止一个线程等待一个它已拥有的互斥对象时出现死锁。不过,为了释放所有权,该线程必须为
每一个等待操作调用一次ReleaseMutex函数。
4.2.3Sleep()
函数功能:该函数对于指定的时间间隔挂起当前的执行线程。
函数原型:voidSleep(DWORDdwMilliseconds);
参数:dwMilliseconds:定义挂起执行线程的时间,以毫秒为单位。取值为0时,该线程将
余下的时间片交给处于就绪状态的同一优先级的其他线程。若没有处于就绪状态的同一优先级
的其他线程,则函数立即返回,该线程继续执行。若取值为INFINITE则造成无限延迟。
返回值:该函数没有返回值。
备注:一个线程可以在调用该函数时将睡眠时间设为0毫秒,以将剩余的时间片交出。使用S
leep函数和直接或间接创建窗口的代码时必须非常小心。若线程创建了窗口,它就必须处理消
息。消息广播被发送给系统中的所有窗口。若有一个线程调用Sleep函数时使用了无限延迟,则
系统会死锁。两个直接创建窗口的代码的例子是DDE和COMCoInitialize。因此,若有一
个创建窗口的线程,则使用MsgWaitForMutipleObjects和MsgWaitForMutipleObjec
tsEx函数,而不使用Sleep()函数。
4.2.4GetTail()
函数功能:获取此列表中的头元素
函数原型:CTypedPtrList::GetTail
参数:指定保存在列表中的元素类型的模板参数
返回值:如果是通过一个指向constCTypedPtrList的指针访问此列表,则GetTail返回
一个类型由模板参数TYPE指定的指针。这使此函数只能被使用在赋值语句的右边,这样就保
护了列表不被修改。
如果列表被直接访问,或通过一个指向CTypedPtrList的指针访问,则GetTail返回对一个类型
由模板参数TYPE指定的指针的引用。这使得此函数可以使用在赋值语句的任何一边,从而允许
该列表可以被修改
备注:在调用GetTail之前,你必须保证该列表不是空的。如果列表是空的,则Microso
ft基础类库的调试版将给出断言。使用IsEmpty来检验列表是否包含元素。
4.3消费者线程
原码:
UINTCMultiThreadDlg::Thread_consumer(LPVOIDparam)//消费者
{
while(threadController)
ﻩ{
ﻩWaitForSingleObject(hMutex,INFINITE);//等待同步事件
ﻩif(number>0)//缓冲区有内容
ﻩ{
ﻩﻩsrand((unsigned)time(NULL));
ﻩﻩintn;
ﻩn=rand()%4+1;
ﻩSleep(n*1000);//延时1-5秒
ﻩﻩCStringstring;
strinmat("%s%d%s%d","0xaa",strlen(ld()),lis
t.GetHead(),sizeof(list.GetTail())^sizeof(strlen(list.GetHead())));//得到缓冲区
内容
ﻩﻩpbox4->AddString(string);
ﻩlist.RemoveHead();//删除头内容
ﻩﻩnumber--;
ﻩReleaseMutex(hMutex);//释放互斥
ﻩ}
else//否则解除互斥
{
ﻩReleaseMutex(hMutex);
}
ﻩ}
ﻩreturn0;
}
原码功能:执行消费费线程,当缓冲区有数据时取出内容,并且按随机时间(1-5秒的范围内)取数,
按固定的格式消息头(1B,固定为0xaa),消息长度(1B),消息内容(nB),校验和(1B)做输出.
4.3.1Sleep()
函数功能:该函数对于指定的时间间隔挂起当前的执行线程。
函数原型:voidSleep(DWORDdwMilliseconds);
参数:dwMilliseconds:定义挂起执行线程的时间,以毫秒为单位。取值为0时,该线程
将余下的时间片交给处于就绪状态的同一优先级的其他线程。若没有处于就绪状态的同一优先
级的其他线程,则函数立即返回,该线程继续执行。若取值为INFINITE则造成无限延迟。
返回值:该函数没有返回值。
备注:一个线程可以在调用该函数时将睡眠时间设为0毫秒,以将剩余的时间片交出。使用Slee
p函数和直接或间接创建窗口的代码时必须非常小心。若线程创建了窗口,它就必须处理消息。
消息广播被发送给系统中的所有窗口。若有一个线程调用Sleep函数时使用了无限延迟,则系
统会死锁。两个直接创建窗口的代码的例子是DDE和COMCoInitialize。因此,若有一个
创建窗口的线程,则使用MsgWaitForMutipleObjects和MsgWaitForMutipleO
bjectsEx函数,而不使用Sleep()函数。
4.3.2ReleaseMutex()
函数功能:该函数放弃指定互斥对象的拥有权。
函数原型:BOOLReleaseMutex(HANDLEhMutex);
参数:hMutex:互斥对象句柄。为CreateMutex或OpenMutex函数的返回值。
返回值:如果函数调用成功,那么返回值是非零值;如果函数调用失败,那么返回值是零值。
若想获得更多错误信息,请调用GetLastError函数。
备注:如果调用线程不拥有互斥对象,ReleaseMutex函数失败。
一个线程通过调用等待函数拥有互斥对象。创建该互斥对象的线程也拥有互斥对象,而不需
要调用等待函数。当互斥对象的所有者线程不再需要互斥对象时,它可以调用ReleaseMutex
函数。
当一个线程拥有一个互斥对象后,它可以用该互斥对象多次调用等待函数而不会阻塞。这防
止一个线程等待一个它已拥有的互斥对象时出现死锁。不过,为了释放所有权,该线程必须为
每一个等待操作调用一次ReleaseMutex函数。
4.3.3WaitForSingleObject
函数功能:当如下情况之一发生时该函数返回:指定对象处于信号态;超时。
函数原型:
DWORDWaitForSingleObject(HANDLEhHandle,DWORDdwMill
iseconds);
参数:hHandle:等待对象句柄。若想了解指定句柄的对象类型列表,参阅下面说明部分。
在WndowsNT中,句柄必须有SYNCHRONIZE访问权限。若想获得更多的信息,请查看
StandardAccessRights。
dwMilliseconds:指定以毫秒为单位的超时间隔。如果超时,即使对象的状态是非信号态
的并且没有完成,函数也返回。如果dwMilliseconds是0,函数测试对象的状态并立刻返回:
如果dwMillseconds是INFINlTE,函数从不超时。
返回值:如果函数调用成功,返回值表明引起函数返回的事件。可能值如下:
WAIT_ABANDONED:指定对象是互斥对象,在线程被终止前,线程没有释放互斥对象。
互斥对象的所属关系被授予调用线程,并且该互斥对象被置为非信号态。
WAIT_OBJECT_0:指定对象的状态被置为信号态。
WAlT_TIMEOUT:超时,并且对象的状态为非信号态。
如果函数调用失败,返回值是WAIT_FAILED。若想获得更多错误信息,请调用GetLastErro
r函数。
4.3.4GetHead
函数功能:用来获取代表此列表中的头元素的指针
函数原型:CTypedPtrList::GetHead
参数:指定保存在列表中的元素类型的模板参数
返回值:如果是通过一个指向constCTypedPtrList的指针访问此列表,则GetHead返
回一个类型由模板参数TYPE指定的指针。这使此函数只能被使用在赋值语句的右边,这样就保
护了列表不被修改。ﻫ如果列表被直接访问,或通过一个指向CTypedPtrList的指针访问,则G
etHead返回对一个类型由模板参数TYPE指定的指针的引用。这使得此函数可以使用在赋值
语句的任何一边,从而允许该列表可以被修改
四.程序主要源码清单
由于代码比较多,在此我给出生产者—消费者中核心部分调试好,可以运行的源码,如果要完整代
码,请查看本程序原整的原代码.
//MultiThreadDl:implementationfile
#include"stdafx.h"
#include"MultiThread.h"
#include"MultiThreadDlg.h"
#include"afxtempl.h"
#include"afxmt.h"
#include"time.h"
#include"stdio.h"
#include"stdlib.h"
#include"winbase.h"
volatileintthreadController;
volatileintcheck;
volatileHANDLEhMutex;
CList<CString,CString&>list;
CListBox*pbox1,*pbox2,*pbox3,*pbox4,*pbox5;
intnumber;
#ifdef_DEBUG
#definenewDEBUG_NEW
#undefTHIS_FILE
staticcharTHIS_FILE[]=__FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
//classCObject;
//CAboutDlgdialogusedforAppAbout
classCAboutDlg:publicCDialog//初始化消息句柄
{
public:
CAboutDlg();
enum{IDD=IDD_ABOUTBOX};
ﻩprotected:
virtualvoidDoDataExchange(CDataExchange*pDX);//Implementation
protected:
ﻩDECLARE_MESSAGE_MAP()
};
voidCAboutDlg::DoDataExchange(CDataExchange*pDX)
{
ﻩCDialog::DoDataExchange(pDX);}
BEGIN_MESSAGE_MAP(CAboutDlg,CDialog)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
CMultiThreadDlg::CMultiThreadDlg(CWnd*pParent/*=NULL*/)
:CDialog(CMultiThreadDlg::IDD,pParent)
{ﻩm_hIcon=AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
voidCMultiThreadDlg::DoDataExchange(CDataExchange*pDX)
{
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CMultiThreadDlg,CDialog)
ﻩON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_START,OnStart)
ﻩON_BN_CLICKED(IDC_STOP,OnStop)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
BOOLCMultiThreadDlg::OnInitDialog()//创建线程
{
ﻩCDialog::OnInitDialog();
ﻩASSERT((IDM_ABOUTBOX&0xFFF0)==IDM_ABOUTBOX);
ﻩASSERT(IDM_ABOUTBOX<0xF000);
ﻩCMenu*pSysMenu=GetSystemMenu(FALSE);
ﻩif(pSysMenu!=NULL)
ﻩ{
ﻩCStringstrAboutMenu;
ﻩstrAboutMenu.LoadString(IDS_ABOUTBOX);
ﻩif(!strAboutMenu.IsEmpty())
ﻩ{
ﻩpSysMenu->AppendMenu(MF_SEPARATOR);
ﻩpSysMenu->AppendMenu(MF_STRING,IDM_ABOUTBOX,strAboutMenu);
ﻩ}
ﻩ}
ﻩ//获取对话框中子窗口控件的句柄
pbox1=(CListBox*)GetDlgItem(IDC_LIST1);
pbox2=(CListBox*)GetDlgItem(IDC_LIST2);
pbox3=(CListBox*)GetDlgItem(IDC_LIST3);
pbox4=(CListBox*)GetDlgItem(IDC_LIST4);
ﻩpbox5=(CListBox*)GetDlgItem(IDC_LIST5);
number=0;
ﻩreturnTRUE;//returnTRUE交出控制权
}
voidCMultiThreadDlg::OnSysCommand(UINTnID,LPARAMlParam)//获取控制命
令
{
if((nID&0xFFF0)==IDM_ABOUTBOX)
ﻩ{
ﻩﻩCAboutDlgdlgAbout;
ﻩdlgAbouModal();
}
else
ﻩ{
CDialog::OnSysCommand(nID,lParam);
}
}
//窗体显示大小
voidCMultiThreadDlg::OnPaint()
{
if(IsIconic())
{
ﻩCPaintDCdc(this);//devicecontextforpainting
ﻩSendMessage(WM_ICONERASEBKGND,(WPARAM)dc.GetSafeHdc(),0);
ﻩintcxIcon=GetSystemMetrics(SM_CXICON);
ﻩintcyIcon=GetSystemMetrics(SM_CYICON);
CRectrect;
ﻩﻩGetClientRect(&rect);
ﻩﻩintx=(rect.Width()-cxIcon+1)/2;
ﻩﻩinty=(rect.Height()-cyIcon+1)/2;
ﻩdc.DrawIcon(x,y,m_hIcon);
}
else
ﻩ{
ﻩﻩCDialog::OnPaint();
}
}
HCURSORCMultiThreadDlg::OnQueryDragIcon()
{
return(HCURSOR)m_hIcon;
}
voidCMultiThreadDlg::OnStart()//开始创建线程
{
ﻩhMutex=CreateMutex(NULL,FALSE,NULL);//初始化
threadController=1;
check=TRUE;
ﻩHWNDhWnd=GetSafeHwnd();//得到控制权
ﻩAfxBeginThread(ThreadProc,hWnd,THREAD_PRIORITY_NORMAL);//启
用生产者线程1(P1)
AfxBeginThread(ThreadProc2,hWnd,THREAD_PRIORITY_NORMAL);//启用
生产者线程2(P2)
AfxBeginThread(ThreadProc3,hWnd,THREAD_PRIORITY_NORMAL);//启用
生产者线程3(P3)
ﻩAfxBeginThread(Thread_consumer,hWnd,THREAD_PRIORITY_NORMAL);
//启用消费者线程1(S1)
AfxBeginThread(Thread_consumer2,hWnd,THREAD_PRIORITY_NORMA
L);//启用消费者线程2(S2)
}
voidCMultiThreadDlg::OnStop()//双击暂时停止所有工作
{
threadController=0;
ﻩcheck=FALSE;
}
UINTCMultiThreadDlg::ThreadProc(LPVOIDparam)//生产者1的生产过程
{
while(threadController)
{
WaitForSingleObject(hMutex,INFINITE);//等待一个同步事件的到来
if(number<20)
ﻩﻩ{
srand((unsigned)time(NULL));//返回一个随机数
ﻩﻩﻩintn;
ﻩn=rand()%9+1;//随机显示字数
ﻩSleep(n*100);
ﻩCStringstr;
ﻩﻩCStringstr_;
for(inti=0;i
ﻩ{
ﻩstr_.Format("%c",65+rand()%9+1);//显示大写字母
ﻩﻩstr.Insert(i,str_);//插入成一列
}
ﻩﻩlist.AddTail(CString(str));
CStringstring;string.Format("%s%d%s%d","0xaa",n,list.Get
Tail(),sizeof(lisil())^sizeof(n));//按格式消息头(1B,固定为0xaa),消息长度
(1B),消息内容(nB),校验和(1B)
ﻩﻩpbox1->AddString(string);//打印
ﻩnumber++;
ﻩ}
ﻩﻩReleaseMutex(hMutex);//释放互斥
ﻩ}
ﻩreturn0;
}
UINTCMultiThreadDlg::ThreadProc2(LPVOIDparam)
{
while(threadController)
ﻩ{
WaitForSingleObject(hMutex,INFINITE);
ﻩif(number<20)
ﻩ{
ﻩsrand((unsigned)time(NULL));
ﻩﻩintn;
ﻩﻩﻩn=rand()%9+1;
ﻩﻩSleep(n*100);
ﻩCStringstr;
ﻩCStringstr_;
ﻩﻩfor(inti=0;i<n;i++)
ﻩ{
ﻩstr_.Format("%c",97+rand()%9+1);
ﻩﻩsert(i,str_);
ﻩ}
ﻩﻩ
ﻩﻩlist.AddTail(CString(str));
ﻩﻩﻩCStringstring;string.Format("%s%d%s%d","0xaa",n,lisTail(),sizeo
f(list.GetTail())^sizeof(n));
ﻩpbox2->AddString(string);
ﻩﻩnumber++;
ﻩ}
ﻩReleaseMutex(hMutex);
}
ﻩreturn0;
}
UINTCMultiThreadDlg::ThreadProc3(LPVOIDparam)
{
ﻩwhile(threadController)
ﻩ{
ﻩWaitForSingleObject(hMutex,INFINITE);//等待一个同步事件的到来
ﻩﻩif(number<20)
ﻩ{
ﻩsrand((unsigned)time(NULL));//返回一个随机数
ﻩintn;
ﻩn=rand()%9+1;
ﻩSleep(n*100);
ﻩCStringstr;
ﻩﻩCStringstr_;
ﻩﻩfor(inti=0;i
ﻩﻩﻩ{
ﻩstr_.Format("%c",48+rand()%9+1);
ﻩﻩﻩstr.Insert(i,str_);
ﻩﻩ}
ﻩdTail(CString(str));
ﻩﻩCStringstring;string.Format("%s%d%s%d","0xaa",n,list.GetTail(),sizeof(li
sTail())^sizeof(n));
ﻩﻩ//lisdHead(CString(string));
pbox3->AddString(string);
ﻩﻩnumber++;
ﻩﻩ}
ﻩﻩReleaseMutex(hMutex);
}
return0;
}
UINTCMultiThreadDlg::Thread_consumer(LPVOIDparam)//消费者
{
while(threadController)
{
WaitForSingleObject(hMutex,INFINITE);//等待同步事件
ﻩif(number>0)//缓冲区有内容
ﻩ{
ﻩsrand((unsigned)time(NULL));
ﻩintn;
ﻩﻩﻩn=rand()%4+1;
ﻩSleep(n*1000);//延时1-5秒
ﻩCStringstring;
ﻩst("%s%d%s%d","0xaa",strlen(list.GetHead()),lisd(),size
of(Tail())^sizeof(strlen(list.GetHead())));//得到缓冲区内容
ﻩpbox4->AddString(string);
ﻩﻩﻩmoveHead();//删除头内容
ﻩnumber--;
ReleaseMutex(hMutex);//释放互斥
ﻩ}
ﻩelse//否则解除互斥
ﻩﻩ{
ﻩReleaseMutex(hMutex);
ﻩ}
}
ﻩreturn0;
}
UINTCMultiThreadDlg::Thread_consumer2(LPVOIDparam)
{
ﻩwhile(threadController)
{
ﻩWaitForSingleObject(hMutex,INFINITE);
ﻩﻩif(number>0)
ﻩ{
ﻩsrand((unsigned)time(NULL));
ﻩﻩintn;
ﻩﻩn=rand()%4+1;
ﻩﻩSleep(n*1000);
ﻩﻩﻩCStringstring;
ﻩstring.Format("%s%d%s%d","0xaa",strlen(list.GetHead()),list.GetHead(),siz
eof(list.GetTail())^sizeof(strlen(lisHead())));
ﻩpbox5->AddString(string);
ﻩﻩlist.RemoveHead();
number--;
ﻩﻩReleaseMutex(hMutex);
ﻩ}
else
ﻩﻩ{
ﻩﻩﻩReleaseMutex(hMutex);
ﻩﻩ}}
return0;}
五.程序界面或运行结果
本程序界面如图5-1所示
图5-1
程序运行结果如图5-2所示
图5-2
六.总结
本次课程设计总整体来说是比较成功的。基本上达到了课程设计的目的。完成了用进程同
步方法解决生产者-消费者问题全部过程,结果满足设计要求,验证无误。生产者-消费者问题不
愧是一个经典的线程(进程)同步问题,形象而且生动的描述了线程(进程)之间的运行状态:一
个仓库可以存放K件物品。生产者每生产一件产品,将产品放入仓库,仓库满了就停止生产。消
费者每次从仓库中去一件物品,然后进行消费,仓库空时就停止消费。在此过程中又采用信号量
机制很好地解决了进程间的同步和互斥这个矛盾。不过在信号量上,自己的确存在着一些问题。
在这次开发的过程中,还遇到了遇到了很多新的问题,比如,对线程的调用和释放,进程的创建和释
放等,对这些系统函数很是生疏,运用过程中不断出现小问题。尽管这样自己还是从中学到了
很多编程的技巧,锻炼了实践动手能手。
通过本次设计,我更近一步地了解了OS的进程机制和信号量实现生产者消费者问题的并发
控制全过程,尤其是对多进程程序设计方法有了更深的理解,及进一步熟悉MFC可视化界面的理
解,以前在书本上学习了有关进程的同步的问题只是理论上的说法,并没有一个实际的东西,至少
应是看得见的,对其含义也只是处于表面上的理解。这次大作业我才体会到线程(进程)同步的真
正涵义。
更多推荐
duplicatehandle
发布评论