ImageVerifierCode 换一换
格式:DOCX , 页数:33 ,大小:218.15KB ,
资源ID:9550616      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/9550616.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(操作系统创建线程利用互斥实现线程共享变量通信.docx)为本站会员(b****8)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

操作系统创建线程利用互斥实现线程共享变量通信.docx

1、操作系统创建线程利用互斥实现线程共享变量通信创建线程,利用互斥实现线程共享变量通信一. 概述1.1 课题目的和意义掌握线程创建和终止,加深对线程和进程概念的理解,会用同步与互斥方法实现线程之间的通信。1.2内容和要求软件界面上点“创建线程” 按钮,创建三个生产者线程(P1,P2,P3)和两个消费者线程(C1,C2),生产者和消费者线程共享一个长度为2KB的环型公共缓冲区,生产者向其中投放消息,消费者从中取走消息。只要缓冲区未满,生产者可将消息送入缓冲区;只要缓冲区未空,消费者可从缓冲区取走一个消息。每个消息具下列结构格式:消息头(1B,固定为0xaa),消息长度(1B),消息内容(nB),校验

2、和(1B),检验和计算方式为消息长度和消息内容所有字节异或结果。每个生产者每隔n毫秒(n用随机数产生,1到100毫秒之间,间隔不固定)生产一个消息加入缓冲区,并把消息产生时间和内容记录在一个文本文件中(或显示在列表框中)。P1每次生产的数据为26个大写字母, P2每次生产的数据为26个小写字母,P3每次生产的数据为10个数字。每个消费者每隔n秒(n用随机数产生,1到5秒之间,间隔不固定)从缓冲区取走一个消息。每消费一个消息需要将消费时间和消息内容记录在一个文本文件中(或显示在列表框中)。当用户按结束按钮时结束5个线程,并将5个文件内容显示出来进行对照。这期实是一个经典的生产者消费者(Produ

3、cer_consumer)进程(线程)同步的问题。它描述的是:有一群生产者进程在生产产品,并将此产品提供给消费者进程(线程)去消费。为使生产者进程和消费者进程(线程)能并发执行,在它们之间设置有个缓冲区的缓冲池,生产者进程(线程)可将它所生产的产品放入一个缓冲区中,消费者进程(线程)可从一个缓冲区取得一个产品消费。尽管所有的生产者进程和消费者进程(线程)都是以异步的方式运行的,但它们之间必须保持同步,即不允许消费者进程(线程)到一个空缓冲区去取产品,也不允许生产者进程(线程)向一个已装有消息尚未被取走产品的缓冲区投放产品。如下图所示:1.3线程所采用的同步方法同步是多线程中的重要概念.同步的使

4、用可以保证在多线程运行的环境中,程充不会产生设计之外的结果.同步的实现方式有两种,同步方法和同步块. 线程在执行同步方法是具有排它性的.当任意一个线和进入到一个对象的任意一个同步方法时,这个对象所有同步方法都被锁定,在些期间,期他任何线程都不能访问这个对象的任意一个同步方法,直到这个线程执行完它所调用的同步方法并从中退出,从而导至它释放了该对象的同步锁这后.在一个对象被某个线程锁定之后,其他线程是可以访问. 同步的有几种实现方法,分别是: wait():使一个线程处于等待状态,并且释放所有持有的对象lock. sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉I

5、nterruptedException异常。notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。1.4开发工具平台 开发平台:window XP 开发工具:VC+二. 数据定义和详细说明1数据定义设计PV操作算法,用信号量机制实现生产者与消费者同步与互斥问题,并与无PV情况下进行对比。定义20个缓冲区,将其初始化为0。调用随机函数rand()生成随机数,把随机数通过生产者放入缓冲区。

6、若缓冲区满则其值非0。当消费者从缓冲区中去数后缓冲区值变为0。程序可显示缓冲区中的全部内容,方便观察生产者与消费者的行为。程序可通过设置sleep(time)中time的值来控制生产者与消费者的执行顺序。2详细说明为了实现生产者与消费者同步与互斥的问题,该程序用记录型信号量机制来实现。假定在生产者与消费者之间,利用一个公共的缓冲池来进行通信,生产者将所生产的信息放入其中,消费者cognitive缓冲池中取得消息来消费,该缓冲池具有n个缓冲区,其编号为0,1,2,3,n-1;设置一个互斥信号量mutex,用于实现诸进程对缓冲池的互斥使用,其初值为1,利用资源信号量empty,表示缓冲池中空缓冲区

7、的数目,其初值为n;full分别表示缓冲池中满缓冲区的数目,其初值为0.又假定这些生产者和消费者相互等效,只要缓冲池未满,生产者便可将消息送入由指针in所指的缓冲区;只要缓冲池未空,消费者变可以从由指针out所指示的缓冲区中,取走一个消息。对生产者消费者的问题可以描述如下:Var metux,empty,full:semaphore=1,n,0;Buffer:array0,n-1 of item;In,out:integer:=0;Begin: Pabegin Producer:begin Repeat Producer an item nextp; P(empty);P(mutex); bu

8、ffer(in):=nextp; in:=(in+1)mod n; V(mutex); V(full);Until false;EndConsumer:begin Repeat P(full);P(mutex); nextc:= buffer(out); out:=(out+1)mod n; V(mutex); V(empty);Until false;EndParendEnd三. 实现思想和设计流程1实现思想我们把系统中使用某一类资源的进程(线程)称为该资源的消费者,而把释放同类资源的进程称为该资源的生产者。例如在计算进程(线程)与打印进程(线程)公用一个缓冲区时,计算进程(线程)把数据送入

9、缓冲区,打印进程(线程)从缓冲区中取数据打印输出,因此,计算进程相当于数据资源的生产者,而打印进程相当于消费者,二者之间必须保持同步。基于这一问题,我们将使用生产者和消费者这一同步机制算法来处理该问题.2设计流程首先,我们知道,生产者消费者问题是一个同步问题。即生产者和消费者之间应满足如下条件:2.1 消费者想接收数据时,有界缓冲区中至少有一个单元是满的。 2.2 生产者想发送数据时,有界缓冲区中至少有一个单元是空的。 另外,由于有界缓冲区是临界资源,因此,各生产者进程和各消费者进程之间必须互斥。其次,我们还必须考虑面临的问题是属于进程互斥还是进程同步,或是互斥与同步的混合问题。然后根据共享资

10、源的数量以及使用共享资源的规则正确的定义信号量及其初值。最后,还要对结果进行分析处理。若结果中生产和消费进程都已处理完时,但还可能出现以下两种情况:一是还有生产进程,但没有空缓冲,且消费进程暂时已完,所以此时,只能结束等待新的消费进程产生空缓冲。二是还有消费进程,但没有了满缓冲,且生产进程暂时已完,此时,只能结束等待新的生产进程来输入数据,产生新的满缓冲等。在程序中应能作出相应的判断和处理。 本程序的执行是在C+的环境中通过手动输入生产者和消费者线程的运行速度来控制程序的运行的。为了实现生产者进程能把生产出来的产品正确的存入缓冲区,和消费者进程能够从缓冲区中取产品进行消费,防止因等待资源而出现

11、死锁的现象,首先设置两个时间:生产者生产一个产品后等待的时间t1,和消费者消费一个产品后等待的时间t2,来控制生产者和消费者进程执行的速度。其函数原形是sleep(t1)和sleep(t2)其中t1、t2指定义挂起执行线程的时间,以毫秒为单位,取值为0时,该线程将余下的时间片交给处于就绪状态的同一优先级的其他线程。若没有处于就绪状态的同一优先级的其他线程,则函数立即返回。 3 程序流程图 图 3-1 程序流程图4关键代码分析 本程序采用了MFC可视化界面来完成,现在给出关键代码来分析.4.1开始创建线程 原代码: void CMultiThreadDlg:OnStart() /开始创建线程 h

12、Mutex=CreateMutex(NULL,FALSE,NULL);/创建互斥对象 threadController=1; check=TRUE;/检测标识 HWND hWnd=GetSafeHwnd();/得到控制权 AfxBeginThread(ThreadProc,hWnd,THREAD_PRIORITY_NORMAL);/启用生产者线程1(P1) AfxBeginThread(ThreadProc2,hWnd,THREAD_PRIORITY_NORMAL);/启用生产者线程2(P2) AfxBeginThread(ThreadProc3,hWnd,THREAD_PRIORITY_NO

13、RMAL);/启用生产者线程3(P3) AfxBeginThread(Thread_consumer,hWnd,THREAD_PRIORITY_NORMAL);/启用消费者线程1(S1) AfxBeginThread(Thread_consumer2,hWnd,THREAD_PRIORITY_NORMAL);/启用消费者线程2(S2)原码功能:主要创建生产者和消费者线程,创建互斥对象,创建窗体对象.主要函数功能:4.1.2 CreateMutex()函数功能:该函数是创建有名或者无名的互斥对象。函数原型:HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMu

14、texAttributes,BOOL bInitialOwner, LPCTSTR lpName);参数:lpMutexAttributes:指向SECURITY_ATTRIBUTES结构的指针,该结构决定子进程是否能继承返回句柄。如果lpMutexAttributes为NULL,那么句柄不能被继承。在Windows NT中该结构的lpSecurityDescriptor成员指定新互斥对象的安全描述符。如果lpMutexAttributes为NULL,那么互斥对象获得缺省的安全描述符。bInitialOwner:指定互斥对象的初始所属身份。如果该值为TRUE并且调用者创建互斥对象,那么调用线程

15、获得互斥对象所属身份。否则,调用线程不能获得互斥对象所属身份。判断调用者是否创建互斥对象请参阅返回值部分。lpName:指向以NULL结尾的字符串,该字符串指定了互斥对象名。该名字的长度小于MAX_PATH且可以包含除反斜线路径分隔符( )以外的任何字符。名字是区分大小写的。如果与已存在的有名互斥对象名相匹配,那么该函数要求用_权限访问已存在的对象。在这种情况下,由于参数己被创建进程所设置,该参数被忽略。如果参数不为,它决定句柄是否解除继承,但是其安全描述符成员被忽略。如果lpName为NULL,那么创建的互斥对象无名。如果lpName与已存在的事件、信号量、可等待定时器、作业、或者文件映射对

16、象的名字相匹配,那么函数调用失败,并且GetLastError函数返回ERPOR_INVALID_HANDLE。其原因是这些对象共享相同的名字空间。返回值:如果函数调用成功,返回值是互斥对象句柄;如果函数调用之前,有名互斥对象已存在,那么函数给已存在的对象返回一个句柄,并且函数GetLastError返回ERROR_ALREADY_EXISTS,否则,调用者创建互斥对象。如果函数调用失败,则返回值为NULL。若想获得更多错误信息,请调用GetLastError函数。如果CreateMutex中的lpMutexAttributes参数允许继承,由CreateProcess函数创建的子进程可以继承

17、父进程的互斥对象句柄。一个进程可以在调用DuplicateHandle函数时指定互斥对象句柄来创建一个可以被其他进程使用的双重句柄。一个进程在调用OpenMutex或CreateMutex函数时能指定互斥对象名。使用CloseHandle函数关闭句柄,进程结束时系统自动关闭句柄。当最后一个句柄被关闭时,互斥对象被销毁。4.1.3 GetSafeHwnd()功能:到一个窗口对象(CWnd的派生对象)指针的句柄(HWND)函数原型: HWND hwnd = pwnd-GetSafeHwnd();函数用法:CWnd *pwnd = FindWindow(“ExploreWClass”,NULL);

18、/希望找到资源管理器HWND hwnd = pwnd-m_hwnd; /得到它的HWND这样的代码当开始得到的pwnd为空的时候就会出现一个“General protection error”,并关闭应用程序,因为一般不能对一个NULL指针访问其成员,如果用下面的代码:CWnd *pwnd = FindWindow(“ExploreWClass”,NULL); /希望找到资源管理器HWND hwnd = pwnd-GetSafeHwnd(); /得到它的HWND就不会出现问题,因为尽管当pwnd是NULL时,GetSafeHwnd仍然可以用,只是返回NULL,通过GetSafeHwnd()的实

19、现代码就更清楚了:_AFXWIN_INLINE HWND CWnd:GetSafeHwnd() constreturn this = NULL?NULL:m_hWnd;4.1.4 AfxBeginThread()功能:用于创建工作者线程函数原型:CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc,LPVOID pParam,int nPriority = THREAD_PRIORITY_NORMAL,UINT nStackSize = 0,DWORD dwCreateFlags = 0,LPSECURITY_ATTRIBUTES l

20、pSecurityAttrs= NULL);返回值: 一个指向新线程的线程对象.pfnThreadProc : 线程的入口函数,声明一定要如下: UINT MyThreadFunction( LPVOID pParam );pParam : 传递入线程的参数,注意它的类型为:LPVOID,所以我们可以传递一个结构体入线程.nPriority : 线程的优先级,一般设置为 0 .让它和主线程具有共同的优先级.nStackSize : 指定新创建的线程的栈的大小.如果为 0,新创建的线程具有和主线程一样的大小的栈dwCreateFlags : 指定创建线程以后,线程有怎么样的标志.可以指定两个值:

21、CREATE_SUSPENDED : 线程创建以后,会处于挂起状态,直到调用:ResumeThread0: 创建线程后就开始运行.lpSecurityAttrs:指向一个 SECURITY_ATTRIBUTES 的结构体,用它来标志新创建线程的安全性.如果为 NULL,那么新创建的线程就具有和主线程一样的安全性.如果要在线程内结束线程,可以在线程内调用 AfxEndThread.4.2生产者线程原码:UINT CMultiThreadDlg:ThreadProc(LPVOID param)/生产者1的生产过程 while(threadController) WaitForSingleObjec

22、t(hMutex,INFINITE);/等待一个同步事件的到来 if(number20) srand (unsigned)time(NULL);/返回一个随机数 int n; n=rand()%9+1;/随机显示字数 Sleep(n*100); CString str; CString str_; for(int i=0; iAddString(string);/打印 number+; ReleaseMutex(hMutex);/释放互斥 原码功能:当生产者线程1得到控制权,等待同步事件,产生随机个数(n),按产生的随机数显示n个大写字母,利用sleep函数来隔时存储到缓冲区,最后按格式消息头

23、(1B,固定为0xaa),消息长度(1B),消息内容(nB),校验和(1B)的方式输出.主要函数功能:4.2.1 WaitForSingleObject()函数功能:当如下情况之一发生时该函数返回:指定对象处于信号态;超时。函数原型: DWORD WaitForSingleObject( HANDLE hHandle,DWORD dwMilliseconds);参数:hHandle:等待对象句柄。若想了解指定句柄的对象类型列表,参阅下面说明部分。在WndowsNT中,句柄必须有SYNCHRONIZE访问权限。若想获得更多的信息,请查看Standard Access Rights。dwMilli

24、seconds:指定以毫秒为单位的超时间隔。如果超时,即使对象的状态是非信号态的并且没有完成,函数也返回。如果dwMilliseconds是0,函数测试对象的状态并立刻返回:如果dwMillseconds是INFINlTE,函数从不超时。返回值:如果函数调用成功,返回值表明引起函数返回的事件。可能值如下:WAIT_ABANDONED:指定对象是互斥对象,在线程被终止前,线程没有释放互斥对象。互斥对象的所属关系被授予调用线程,并且该互斥对象被置为非信号态。WAIT_OBJECT_0:指定对象的状态被置为信号态。WAlT_TIMEOUT:超时,并且对象的状态为非信号态。如果函数调用失败,返回值是W

25、AIT_FAILED。若想获得更多错误信息,请调用GetLastError函数。4.2.2 ReleaseMutex()函数功能:该函数放弃指定互斥对象的拥有权。函数原型:BOOL ReleaseMutex(HANDLE hMutex);参数:hMutex:互斥对象句柄。为CreateMutex或OpenMutex函数的返回值。返回值:如果函数调用成功,那么返回值是非零值;如果函数调用失败,那么返回值是零值。若想获得更多错误信息,请调用GetLastError函数。备注:如果调用线程不拥有互斥对象,ReleaseMutex函数失败。一个线程通过调用等待函数拥有互斥对象。创建该互斥对象的线程也拥

26、有互斥对象,而不需要调用等待函数。当互斥对象的所有者线程不再需要互斥对象时,它可以调用ReleaseMutex函数。当一个线程拥有一个互斥对象后,它可以用该互斥对象多次调用等待函数而不会阻塞。这防止一个线程等待一个它已拥有的互斥对象时出现死锁。不过,为了释放所有权,该线程必须为每一个等待操作调用一次ReleaseMutex函数。4.2.3 Sleep()函数功能:该函数对于指定的时间间隔挂起当前的执行线程。函数原型:void Sleep(DWORD dwMilliseconds);参数:dwMilliseconds:定义挂起执行线程的时间,以毫秒为单位。取值为0时,该线程将余下的时间片交给处于

27、就绪状态的同一优先级的其他线程。若没有处于就绪状态的同一优先级的其他线程,则函数立即返回,该线程继续执行。若取值为INFINITE则造成无限延迟。返回值:该函数没有返回值。备注:一个线程可以在调用该函数时将睡眠时间设为0毫秒,以将剩余的时间片交出。使用Sleep函数和直接或间接创建窗口的代码时必须非常小心。若线程创建了窗口,它就必须处理消息。消息广播被发送给系统中的所有窗口。若有一个线程调用Sleep函数时使用了无限延迟,则系统会死锁。两个直接创建窗口的代码的例子是DDE和COM CoInitialize。因此,若有一个创建窗口的线程,则使用MsgWaitForMutipleObjects和M

28、sgWaitForMutipleObjectsEx函数,而不使用Sleep()函数。4.2.4 GetTail()函数功能:获取此列表中的头元素函数原型: CTypedPtrList:GetTail参数: 指定保存在列表中的元素类型的模板参数返回值: 如果是通过一个指向const CTypedPtrList的指针访问此列表,则GetTail返回一个类型由模板参数TYPE指定的指针。这使此函数只能被使用在赋值语句的右边,这样就保护了列表不被修改。如果列表被直接访问,或通过一个指向CTypedPtrList的指针访问,则GetTail返回对一个类型由模板参数TYPE指定的指针的引用。这使得此函数可

29、以使用在赋值语句的任何一边,从而允许该列表可以被修改 备注:在调用GetTail之前,你必须保证该列表不是空的。如果列表是空的,则Microsoft基础类库的调试版将给出断言。使用IsEmpty来检验列表是否包含元素。4.3 消费者线程原码: UINT CMultiThreadDlg:Thread_consumer(LPVOID param)/消费者 while(threadController) WaitForSingleObject(hMutex,INFINITE);/等待同步事件 if(number0)/缓冲区有内容 srand (unsigned)time(NULL); int n;

30、n=rand()%4+1; Sleep(n*1000);/延时1-5秒 CString string; string.Format(%s%d%s%d,0xaa,strlen(list.GetHead(),list.GetHead(),sizeof(list.GetTail()sizeof(strlen(list.GetHead();/得到缓冲区内容 pbox4-AddString(string); list.RemoveHead();/删除头内容 number-; ReleaseMutex(hMutex);/释放互斥 else/否则解除互斥 ReleaseMutex(hMutex); return 0;原码功能:执行消费费线程,当缓冲区有数据时取出内容,并且按随机时间(1-5秒的范围内)取数,按固定的格式消息头(1B,固定为0xa

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1