📄 chap12_2.htm
字号:
<html><head><meta http-equiv="Content-Type" content="text/html; charset=gb2312"><meta name="GENERATOR" content="Microsoft FrontPage 3.0"><title>12.2 线程的同步</title><link rel="stylesheet" href="../../../cpcw.css"></head><body link="#3973DE" alink="#3973DE" background="../../bg.gif"><div align="center"><center> <table width="85%" border="0"> <tr bgcolor="#FFFFFF"> <td> <div align="center"> <center> </center> </div> <p align="CENTER"><b><font color="red"><font face="Times New Roman">12.2 </font>线程的同步</font></b></p> <p align="CENTER"> </p> <p align="JUSTIFY"> 多线程的使用会产生一些新的问题,主要是如何保证线程的同步执行。多线程应用程序需要使用同步对象和等待函数来实现同步。</p> <p align="JUSTIFY"> <b></b><font color="#3973DE" face="Times New Roman">12.2.1 </font><font color="#3973DE">为什么需要同步</font></p> <p align="JUSTIFY"> 由于同一进程的所有线程共享进程的虚拟地址空间,并且线程的中断是汇编语言级的,所以可能会发生两个线程同时访问同一个对象(包括全局变量、共享资源、API函数和MFC对象等)的情况,这有可能导致程序错误。例如,如果一个线程在未完成对某一大尺寸全局变量的读操作时,另一个线程又对该变量进行了写操作,那么第一个线程读入的变量值可能是一种修改过程中的不稳定值。</p> <p align="JUSTIFY"> 属于不同进程的线程在同时访问同一内存区域或共享资源时,也会存在同样的问题。</p> <p align="JUSTIFY"> 因此,在多线程应用程序中,常常需要采取一些措施来同步线程的执行。需要同步的情况包括以下几种:</p> <blockquote> <blockquote> <p align="JUSTIFY">在多个线程同时访问同一对象时,可能产生错误。例如,如果当一个线程正在读取一个至关重要的共享缓冲区时,另一个线程向该缓冲区写入数据,那么程序的运行结果就可能出错。程序应该尽量避免多个线程同时访问同一个缓冲区或系统资源。</p> <p align="JUSTIFY">在Windows 95环境下编写多线程应用程序还需要考虑重入问题。Windows NT是真正的32位操作系统,它解决了系统重入问题。而Windows 95由于继承了Windows 3.x的部分16位代码,没能够解决重入问题。这意味着在Windows 95中两个线程不能同时执行某个系统功能,否则有可能造成程序错误,甚至会造成系统崩溃。应用程序应该尽量避免发生两个以上的线程同时调用同一个Windows API函数的情况。</p> <p align="JUSTIFY">由于大小和性能方面的原因,MFC对象在对象级不是线程安全的,只有在类级才是。也就是说,两个线程可以安全地使用两个不同的CString对象,但同时使用同一个CString对象就可能产生问题。如果必须使用同一个对象,那么应该采取适当的同步措施。</p> <p align="JUSTIFY">多个线程之间需要协调运行。例如,如果第二个线程需要等待第一个线程完成到某一步时才能运行,那么该线程应该暂时挂起以减少对CPU的占用时间,提高程序的执行效率。当第一个线程完成了相应的步骤后,应该发出某种信号来激活第二个线程。</p> </blockquote> </blockquote> <p><b> </b></p> <p align="JUSTIFY"> <b></b><font color="#3973DE" face="Times New Roman">12.2.2 </font><font color="#3973DE">等待函数</font></p> <p align="JUSTIFY"> Win32 API提供了一组能使线程阻塞其自身执行的等待函数。这些函数只有在作为其参数的一个或多个同步对象(见下小节)产生信号时才会返回。在超过规定的等待时间后,不管有无信号,函数也都会返回。在等待函数未返回时,线程处于等待状态,此时线程只消耗很少的CPU时间。</p> <p align="JUSTIFY"> 使用等待函数即可以保证线程的同步,又可以提高程序的运行效率。最常用的等待函数是WaitForSingleObject,该函数的声明为:</p> <blockquote> <blockquote> <blockquote> <p align="JUSTIFY">DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);</p> </blockquote> </blockquote> </blockquote> <p> 参数hHandle是同步对象的句柄。参数dwMilliseconds是以毫秒为单位的超时间隔,如果该参数为0,那么函数就测试同步对象的状态并立即返回,如果该参数为INFINITE,则超时间隔是无限的。函数的返回值在表12.1中列出。</p> <p align="JUSTIFY"><b> </b></p> <b> <p align="CENTER">表12.1 WaitForSingleObject的返回值</p> </b> <table border="1" cellspacing="1" cellpadding="1" width="579"> <tr> <td width="27%"><b> <p align="JUSTIFY">返回值 </b></td> <td width="73%"><b> <p align="JUSTIFY">含义 </b></td> </tr> <tr> <td width="27%"> <p align="JUSTIFY">WAIT_FAILED </td> <td width="73%"> <p align="JUSTIFY">函数失败 </td> </tr> <tr> <td width="27%"> <p align="JUSTIFY">WAIT_OBJECT_0 </td> <td width="73%"> <p align="JUSTIFY">指定的同步对象处于有信号的状态 </td> </tr> <tr> <td width="27%"> <p align="JUSTIFY">WAIT_ABANDONED </td> <td width="73%"> <p align="JUSTIFY">拥有一个mutex的线程已经中断了,但未释放该MUTEX </td> </tr> <tr> <td width="27%"> <p align="JUSTIFY">WAIT_TIMEOUT </td> <td width="73%"> <p align="JUSTIFY">超时返回,并且同步对象无信号 </td> </tr> </table> <p> </p> <p align="JUSTIFY"> </p> <p align="JUSTIFY">函数WaitForMultipleObjects可以同时监测多个同步对象,该函数的声明为:</p> <blockquote> <blockquote> <blockquote> <p align="JUSTIFY">DWORD WaitForMultipleObjects(DWORD nCount, CONST HANDLE *lpHandles, BOOL bWaitAll, DWORD dwMilliseconds );</p> </blockquote> </blockquote> </blockquote> <p> </p> <p align="JUSTIFY"> 参数nCount是句柄数组中句柄的数目。lpHandles代表一个句柄数组。bWaitAll说明了等待类型,如果为TRUE,那么函数在所有对象都有信号后才返回,如果为FALSE,则只要有一个对象变成有信号的,函数就返回。函数的返回值在表12.2中列出。参数dwMilliseconds是以毫秒为单位的超时间隔,如果该参数为0,那么函数就测试同步对象的状态并立即返回,如果该参数为INFINITE,则超时间隔是无限的。</p> <p align="JUSTIFY"><b> </b></p> <b> <p align="CENTER">表12.2 WaitForMultipleObjects的返回值</p> </b> <table border="1" cellspacing="1" cellpadding="1" width="579"> <tr> <td width="35%"><b> <p align="JUSTIFY">返回值 </b></td> <td width="65%"><b> <p align="JUSTIFY">说明 </b></td> </tr> <tr> <td width="35%"> <p align="JUSTIFY">WAIT_OBJECT_0到WAIT_ OBJECT_0+nCount-1 </td> <td width="65%"> <p align="JUSTIFY">若bWaitAll为TRUE,则返回值表明所有对象都是有信号的。如果bWaitAll为FALSE,则返回值减去WAIT_OBJECT_0就是数组中有信号对象的最小索引。 </td> </tr> <tr> <td width="35%"> <p align="JUSTIFY">WAIT_ABANDONED_0到WAIT_ ABANDONED_ 0+nCount-1
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -