📄 chap12_2.htm
字号:
<td WIDTH="65%"><font SIZE="3"><p ALIGN="JUSTIFY">若</font><font FACE="Times New Roman" SIZE="3">bWaitAll</font><font SIZE="3">为</font><font FACE="Times New Roman" SIZE="3">TRUE</font><font SIZE="3">,则返回值表明所有对象都有信号,但有一个</font><font FACE="Times New Roman" SIZE="3">mutex</font><font SIZE="3">被放弃了。若</font><font FACE="Times New Roman" SIZE="3">bWaitAll</font><font SIZE="3">为</font><font FACE="Times New Roman" SIZE="3">FALSE</font><font SIZE="3">,则返回值减去</font><font FACE="Times New Roman" SIZE="3">WAIT_ABANDONED_0</font><font SIZE="3">就是被放弃</font><font FACE="Times New Roman" SIZE="3">mutex</font><font SIZE="3">在对象数组中的索引。</font></td>
</tr>
<tr>
<td WIDTH="35%"><font FACE="Times New Roman" SIZE="3"><p ALIGN="JUSTIFY">WAIT_TIMEOUT</font></td>
<td WIDTH="65%"><font SIZE="3"><p ALIGN="JUSTIFY">超时返回。</font></td>
</tr>
</table>
<font FACE="Times New Roman" SIZE="3"><p></font><b><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY"></font></b><font color="#3973DE" FACE="Times New Roman" SIZE="3">12.2.3
</font><font SIZE="3" color="#3973DE">同步对象</font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY"></font><font SIZE="3">同步对象用来协调多线程的执行,它可以被多个线程共享。线程的等待函数用同步对象的句柄作为参数,同步对象应该是所有要使用的线程都能访问到的。同步对象的状态要么是有信号的,要么是无信号的。同步对象主要有三种:事件、</font><font FACE="Times New Roman" SIZE="3">mutex</font><font SIZE="3">和信号灯。</font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY"></font><font SIZE="3">事件对象</font><font FACE="Times New Roman" SIZE="3">(Event)</font><font SIZE="3">是最简单的同步对象,它包括有信号和无信号两种状态。在线程访问某一资源之前,也许需要等待某一事件的发生,这时用事件对象最合适。例如,只有在通信端口缓冲区收到数据后,监视线程才被激活。</font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY"></font><font SIZE="3">事件对象是用</font><font FACE="Times New Roman" SIZE="3">CreateEvent</font><font SIZE="3">函数建立的。该函数可以指定事件对象的种类和事件的初始状态。如果是手工重置事件,那么它总是保持有信号状态,直到用</font><font FACE="Times New Roman" SIZE="3">ResetEvent</font><font SIZE="3">函数重置成无信号的事件。如果是自动重置事件,那么它的状态在单个等待线程释放后会自动变为无信号的。用</font><font FACE="Times New Roman" SIZE="3">SetEvent</font><font SIZE="3">可以把事件对象设置成有信号状态。在建立事件时,可以为对象起个名字,这样其它进程中的线程可以用</font><font FACE="Times New Roman" SIZE="3">OpenEvent</font><font SIZE="3">函数打开指定名字的事件对象句柄。</font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY">mutex</font><font SIZE="3">对象的状态在它不被任何线程拥有时是有信号的,而当它被拥有时则是无信号的。</font><font FACE="Times New Roman" SIZE="3">mutex</font><font SIZE="3">对象很适合用来协调多个线程对共享资源的互斥访问</font><font FACE="Times New Roman" SIZE="3">(mutually exclusive)</font><font SIZE="3">。</font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY"></font><font SIZE="3">线程用</font><font FACE="Times New Roman" SIZE="3">CreateMutex</font><font SIZE="3">函数来建立</font><font FACE="Times New Roman" SIZE="3">mutex</font><font SIZE="3">对象,在建立</font><font FACE="Times New Roman" SIZE="3">mutex</font><font SIZE="3">时,可以为对象起个名字,这样其它进程中的线程可以用</font><font FACE="Times New Roman" SIZE="3">OpenMutex</font><font SIZE="3">函数打开指定名字的</font><font FACE="Times New Roman" SIZE="3">mutex</font><font SIZE="3">对象句柄。在完成对共享资源的访问后,线程可以调用</font><font FACE="Times New Roman" SIZE="3">ReleaseMutex</font><font SIZE="3">来释放</font><font FACE="Times New Roman" SIZE="3">mutex</font><font SIZE="3">,以便让别的线程能访问共享资源。如果线程终止而不释放</font><font FACE="Times New Roman" SIZE="3">mutex</font><font SIZE="3">,则认为该</font><font FACE="Times New Roman" SIZE="3">mutex</font><font SIZE="3">被废弃。</font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY"></font><font SIZE="3">信号灯对象维护一个从</font><font FACE="Times New Roman" SIZE="3">0</font><font SIZE="3">开始的计数,在计数值大于</font><font FACE="Times New Roman" SIZE="3">0</font><font SIZE="3">时对象是有信号的,而在计数值为</font><font FACE="Times New Roman" SIZE="3">0</font><font SIZE="3">时则是无信号的。信号灯对象可用来限制对共享资源进行访问的线程数量。线程用</font><font FACE="Times New Roman" SIZE="3">CreateSemaphore</font><font SIZE="3">函数来建立信号灯对象,在调用该函数时,可以指定对象的初始计数和最大计数。在建立信号灯时也可以为对象起个名字,别的进程中的线程可以用</font><font FACE="Times New Roman" SIZE="3">OpenSemaphore</font><font SIZE="3">函数打开指定名字的信号灯句柄。</font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY"></font><font SIZE="3">一般把信号灯的初始计数设置成最大值。每次当信号灯有信号使等待函数返回时,信号灯计数就会减</font><font FACE="Times New Roman" SIZE="3">1</font><font SIZE="3">,而调用</font><font FACE="Times New Roman" SIZE="3">ReleaseSemaphore</font><font SIZE="3">可以增加信号灯的计数。计数值越小就表明访问共享资源的程序越多。</font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY"></font><font SIZE="3">除了上述三种同步对象外,表</font><font FACE="Times New Roman" SIZE="3">12.3</font><font SIZE="3">中的对象也可用于同步。另外,有时可以用文件或通信设备作为同步对象使用。</font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY"></font><b><font SIZE="3"> </p>
<p ALIGN="CENTER">表</font><font FACE="Times New Roman" SIZE="3">12.3 </font><font SIZE="3">可用于同步的对象</font></b></p>
<table BORDER="1" CELLSPACING="1" CELLPADDING="1" WIDTH="579">
<tr>
<td WIDTH="22%"><font SIZE="3"><b><p ALIGN="JUSTIFY">对象</b></font></td>
<td WIDTH="78%"><font SIZE="3"><b><p ALIGN="JUSTIFY">描述</b></font></td>
</tr>
<tr>
<td WIDTH="22%"><font SIZE="3"><p ALIGN="JUSTIFY">变化通知</font></td>
<td WIDTH="78%"><font SIZE="3"><p ALIGN="JUSTIFY">由</font><font FACE="Times New Roman" SIZE="3">FindFirstChangeNotification</font><font SIZE="3">函数建立,当在指定目录中发生指定类型的变化时对象变成有信号的。</font></td>
</tr>
<tr>
<td WIDTH="22%"><font SIZE="3"><p ALIGN="JUSTIFY">控制台输入</font></td>
<td WIDTH="78%"><font SIZE="3"><p ALIGN="JUSTIFY">在控制台建立是被创建。它是用</font><font FACE="Times New Roman" SIZE="3">CONIN$</font><font SIZE="3">调用</font><font FACE="Times New Roman" SIZE="3">CreateFile</font><font SIZE="3">函数返回的句柄,或是</font><font FACE="Times New Roman" SIZE="3">GetStdHandle</font><font SIZE="3">函数的返回句柄。如果控制台输入缓冲区中有数据,那么对象是有信号的,如果缓冲区为空,则对象是无信号的。</font></td>
</tr>
<tr>
<td WIDTH="22%"><font SIZE="3"><p ALIGN="JUSTIFY">进程</font></td>
<td WIDTH="78%"><font SIZE="3"><p ALIGN="JUSTIFY">当调用</font><font FACE="Times New Roman" SIZE="3">CreateProcess</font><font SIZE="3">建立进程时被创建。进程在运行时对象是无信号的,当进程终止时对象是有信号的。</font></td>
</tr>
<tr>
<td WIDTH="22%"><font SIZE="3"><p ALIGN="JUSTIFY">线程</font></td>
<td WIDTH="78%"><font SIZE="3"><p ALIGN="JUSTIFY">当调用</font><font FACE="Times New Roman" SIZE="3">Createprocess</font><font SIZE="3">、</font><font FACE="Times New Roman" SIZE="3">CreateThread</font><font SIZE="3">或</font><font FACE="Times New Roman" SIZE="3">CreateRemoteThread</font><font SIZE="3">函数创建新线程时被创建。在线程运行是对象是无信号的,在线程终止时则是有信号的。</font></td>
</tr>
</table>
<p><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY"></font><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY"></font><font SIZE="3">当对象不再使用时,应该用</font><font FACE="Times New Roman" SIZE="3">CloseHandle</font><font SIZE="3">函数关闭对象句柄。</font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY"></font><font SIZE="3">清单</font><font FACE="Times New Roman" SIZE="3">12.3</font><font SIZE="3">是一个使用事件对象的简单例子,在该例中,假设主线程要读取共享缓冲区中的内容,而辅助线程负责向缓冲区中写入数据。两个线程使用了一个</font><font FACE="Times New Roman" SIZE="3">hEvent</font><font SIZE="3">事件对象来同步。在用</font><font FACE="Times New Roman" SIZE="3">CreateEvent</font><font SIZE="3">函数创建事件对象句柄时,指定该对象是一个自动重置事件,其初始状态为有信号的。当线程要读写缓冲区时,调用</font><font FACE="Times New Roman" SIZE="3">WaitForSingleObject</font><font SIZE="3">函数无限等待</font><font FACE="Times New Roman" SIZE="3">hEvent</font><font SIZE="3">信号。如果</font><font FACE="Times New Roman" SIZE="3">hEvent</font><font SIZE="3">无信号,则说明另一线程正在访问缓冲区;如果有信号,则本线程可以访问缓冲区,</font><font FACE="Times New Roman" SIZE="3">WaitForSingleObject</font><font SIZE="3">函数在返回后会自动把</font><font FACE="Times New Roman" SIZE="3">hEvent</font><font SIZE="3">置成无信号的,这样在本线程读写缓冲区时别的线程不会同时访问。在完成读写操作后,调用</font><font FACE="Times New Roman" SIZE="3">SetEvent</font><font SIZE="3">函数把</font><font FACE="Times New Roman" SIZE="3">hEvent</font><font SIZE="3">置成有信号的,以使别的线程有机会访问共享缓冲区。</font><b><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY"></font><font SIZE="3"> </p>
<p ALIGN="JUSTIFY">清单</font><font FACE="Times New Roman" SIZE="3">12.3 </font></b><font SIZE="3">使用事件对象的简单例子</font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY">HANDLE hEvent; //</font><font SIZE="3">全局变量</font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY"></font><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY">//</font><font SIZE="3">主线程</font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY">hEvent=CreateEvent(NULL, FALSE, TRUE, NULL);</p>
<p ALIGN="JUSTIFY">if(hEvent= =NULL) return;</p>
<p></font><b><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY">. . .</b></p>
<p ALIGN="JUSTIFY">WaitForSingleObject(hEvent, INFINITE);</p>
<p ALIGN="JUSTIFY">ReadFromBuf( );</p>
<p ALIGN="JUSTIFY">SetEvent( hEvent );</p>
<p></font><b><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY">. . .</b></p>
<p ALIGN="JUSTIFY">CloseHandle( hEvent );</p>
<p ALIGN="JUSTIFY"></font><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY"></font><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY">//</font><font SIZE="3">辅助线程</font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY">UINT MyThreadProc( LPVOID pParam )</p>
<p ALIGN="JUSTIFY">{</p>
<p ALIGN="JUSTIFY"><b>. . .</b></p>
<p ALIGN="JUSTIFY">WaitForSingleObject(hEvent, INFINITE);</p>
<p ALIGN="JUSTIFY">WriteToBuf( );</p>
<p ALIGN="JUSTIFY">SetEvent( hEvent );</p>
<p ALIGN="JUSTIFY"><b>. . .</p>
<p ALIGN="JUSTIFY"></b>return 0; // </font><font SIZE="3">线程正常结束</font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY">}</p>
<b><p ALIGN="JUSTIFY"></b></font><font color="#3973DE" FACE="Times New Roman" SIZE="3">12.2.4
</font><font SIZE="3" color="#3973DE">关键节和互锁变量访问</font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY"></font><font SIZE="3">关键节</font><font FACE="Times New Roman" SIZE="3">(Critical Seciton)</font><font SIZE="3">与</font><font FACE="Times New Roman" SIZE="3">mutex</font><font SIZE="3">的功能类似,但它只能由同一进程中的线程使用。关键节可以防止共享资源被同时访问。</font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY"></font><font SIZE="3">进程负责为关键节分配内存空间,关键节实际上是一个</font><font FACE="Times New Roman" SIZE="3">CRITICAL_SECTION</font><font SIZE="3">型的变量,它一次只能被一个线程拥有。在线程使用关键节之前,必须调用</font><font FACE="Times New Roman" SIZE="3">InitializeCriticalSection</font><font SIZE="3">函数将其初始化。如果线程中有一段关键的代码不希望被别的线程中断,那么可以调用</font><font FACE="Times New Roman" SIZE="3">EnterCriticalSection</font><font SIZE="3">函数来申请关键节的所有权,在运行完关键代码后再用</font><font FACE="Times New Roman" SIZE="3">LeaveCriticalSection</font><font SIZE="3">函数来释放所有权。如果在调用</font><font FACE="Times New Roman" SIZE="3">EnterCriticalSection</font><font SIZE="3">时关键节对象已被另一个线程拥有,那么该函数将无限期等待所有权。</font><font FACE="Times New Roman" SIZE="3"></p>
<p ALIGN="JUSTIFY"></font><font SIZE="3">利用互锁变量可以建立简单有效的同步机制。使用函数</font><font FACE="Times New Roman" SIZE="3">InterlockedIncrement</font><font SIZE="3">和</font><font FACE="Times New Roman" SIZE="3">InterlockedDecrement</font><font SIZE="3">可以增加或减少多个线程共享的一个</font><font FACE="Times New Roman" SIZE="3">32</font><font SIZE="3">位变量的值,并且可以检查结果是否为</font><font FACE="Times New Roman" SIZE="3">0</font><font SIZE="3">。线程不必担心会被其它线程中断而导致错误。如果变量位于共享内存中,那么不同进程中的线程也可以使用这种机制。</font><font FACE="Times New Roman" SIZE="3"></p>
</font><div align="center"><center><table border="0" cellpadding="0" cellspacing="0" width="615">
<tr>
<td><a href="chap12_1.htm">上一页</a></td>
<td><p align="right"><a href="chap12_3.htm">下一页</a></td>
</tr>
</table>
</center></div><font SIZE="5"><hr noshade color="#3973DE" size="1">
<p align="center"></font><font size="2" color="#000000">本教程由<a href="http://vcdynasty.yeah.net">Visual C++王朝(Where programmers come together)</a>协助制作<br>
未经许可,请勿以任何形式复制</font></td>
<b>
</tr>
</table>
</center></div>
<p ALIGN="CENTER"></b><font SIZE="5"> </font><font FACE="Times New Roman" SIZE="5"></p>
</font><font FACE="Times New Roman" SIZE="3">
<p></font><b><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
</font></b>
<p><font SIZE="3"> </font><font FACE="Times New Roman" SIZE="3"></p>
<p></font> </p>
</body>
</html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -