﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-Tinysun-随笔分类-Win32API 编程</title><link>http://www.blogjava.net/tinysun/category/37797.html</link><description /><language>zh-cn</language><lastBuildDate>Fri, 20 Mar 2009 13:38:49 GMT</lastBuildDate><pubDate>Fri, 20 Mar 2009 13:38:49 GMT</pubDate><ttl>60</ttl><item><title>Win32API之进程相关</title><link>http://www.blogjava.net/tinysun/archive/2009/03/20/261103.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Fri, 20 Mar 2009 10:01:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2009/03/20/261103.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/261103.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2009/03/20/261103.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/261103.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/261103.html</trackback:ping><description><![CDATA[<p align="right">&nbsp;</p>
<p><font size="3"><font color="#000000"><strong>从别人BLOG上看到的东西~~很有用~~记下来~！！</strong></font></font></p>
<p><font size="3"><font color="#000000"><strong>CancelWaitableTimer 这个函数用于取消一个可以等待下去的计时器操作 <br />
CallNamedPipe 这个函数由一个希望通过管道通信的一个客户进程调用 <br />
ConnectNamedPipe 指示一台服务器等待下去，直至客户机同一个命名管道连接 <br />
CreateEvent 创建一个事件对象 <br />
CreateMailslot 创建一个邮路。返回的句柄由邮路服务器使用（收件人） <br />
CreateMutex 创建一个互斥体（MUTEX） <br />
CreateNamedPipe 创建一个命名管道。返回的句柄由管道的服务器端使用 <br />
CreatePipe 创建一个匿名管道 <br />
CreateProcess 创建一个新进程（比如执行一个程序） <br />
CreateSemaphore 创建一个新的信号机 <br />
CreateWaitableTimer 创建一个可等待的计时器对象 <br />
DisconnectNamedPipe 断开一个客户与一个命名管道的连接 <br />
DuplicateHandle 在指出一个现有系统对象当前句柄的情况下，为那个对象创建一个新句柄 <br />
ExitProcess 中止一个进程 <br />
FindCloseChangeNotification 关闭一个改动通知对象 <br />
FindExecutable 查找与一个指定文件关联在一起的程序的文件名 <br />
</strong>FindFirstChangeNotification 创建一个文件通知对象。该对象用于监视文件系统发生的变化 <br />
FindNextChangeNotification 重设一个文件改变通知对象，令其继续监视下一次变化 <br />
FreeLibrary 释放指定的动态链接库 <br />
GetCurrentProcess 获取当前进程的一个伪句柄 <br />
GetCurrentProcessId 获取当前进程一个唯一的标识符 <br />
GetCurrentThread 获取当前线程的一个伪句柄 <br />
GetCurrentThreadId 获取当前线程一个唯一的线程标识符 <br />
GetExitCodeProces 获取一个已中断进程的退出代码 <br />
GetExitCodeThread 获取一个已中止线程的退出代码 <br />
GetHandleInformation 获取与一个系统对象句柄有关的信息 <br />
GetMailslotInfo 获取与一个邮路有关的信息 <br />
GetModuleFileName 获取一个已装载模板的完整路径名称 <br />
GetModuleHandle 获取一个应用程序或动态链接库的模块句柄 <br />
GetPriorityClass 获取特定进程的优先级别 <br />
GetProcessShutdownParameters 调查系统关闭时一个指定的进程相对于其它进程的关闭早迟情况 <br />
GetProcessTimes 获取与一个进程的经过时间有关的信息 <br />
GetProcessWorkingSetSize 了解一个应用程序在运行过程中实际向它交付了多大容量的内存 <br />
GetSartupInfo 获取一个进程的启动信息 <br />
GetThreadPriority 获取特定线程的优先级别 <br />
GetTheardTimes 获取与一个线程的经过时间有关的信息 <br />
GetWindowThreadProcessId 获取与指定窗口关联在一起的一个进程和线程标识符 <br />
LoadLibrary 载入指定的动态链接库，并将它映射到当前进程使用的地址空间 <br />
LoadLibraryEx 装载指定的动态链接库，并为当前进程把它映射到地址空间 <br />
LoadModule 载入一个Windows应用程序，并在指定的环境中运行 <br />
MsgWaitForMultipleObjects 等侯单个对象或一系列对象发出信号。如返回条件已经满足，则立即返回 <br />
SetPriorityClass 设置一个进程的优先级别 <br />
SetProcessShutdownParameters 在系统关闭期间，为指定进程设置他相对于其它程序的关闭顺序 <br />
SetProcessWorkingSetSize 设置操作系统实际划分给进程使用的内存容量 <br />
SetThreadPriority 设定线程的优先级别 <br />
ShellExecute 查找与指定文件关联在一起的程序的文件名 <br />
TerminateProcess 结束一个进程 <br />
WinExec 运行指定的程序 <br />
</p>
</font></font>
<img src ="http://www.blogjava.net/tinysun/aggbug/261103.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2009-03-20 18:01 <a href="http://www.blogjava.net/tinysun/archive/2009/03/20/261103.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入浅出Win32多线程程序设计之线程通信(转)</title><link>http://www.blogjava.net/tinysun/archive/2008/11/30/243521.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Sun, 30 Nov 2008 05:25:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2008/11/30/243521.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/243521.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2008/11/30/243521.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/243521.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/243521.html</trackback:ping><description><![CDATA[<strong>简介<br />
<br />
</strong>　　线程之间通信的两个基本问题是互斥和同步。<br />
<br />
　　线程同步是指线程之间所具有的一种制约关系，一个线程的执行依赖另一个线程的消息，当它没有得到另一个线程的消息时应等待，直到消息到达时才被唤醒。<br />
<br />
　　线程互斥是指对于共享的操作系统资源（指的是广义的"资源"，而不是Windows的.res文件，譬如全局变量就是一种共享资源），在各线程访问时的排它性。当有若干个线程都要使用某一共享资源时，任何时刻最多只允许一个线程去使用，其它要使用该资源的线程必须等待，直到占用资源者释放该资源。<br />
<br />
　　线程互斥是一种特殊的线程同步。<br />
<br />
　　实际上，互斥和同步对应着线程间通信发生的两种情况：<br />
<br />
　　（1）当有多个线程访问共享资源而不使资源被破坏时；<br />
<br />
　　（2）当一个线程需要将某个任务已经完成的情况通知另外一个或多个线程时。<br />
<br />
　　在WIN32中，同步机制主要有以下几种：<br />
<br />
　　（1）事件(Event);<br />
<br />
　　（2）信号量(semaphore);<br />
<br />
　　（3）互斥量(mutex);<br />
<br />
　　（4）临界区(Critical section)。<br />
<br />
　　<strong>全局变量</strong><br />
<br />
　　因为进程中的所有线程均可以访问所有的全局变量，因而全局变量成为Win32多线程通信的最简单方式。例如：<br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>int var; //全局变量<br />
            UINT ThreadFunction(LPVOIDpParam)<br />
            {<br />
            　var = 0;<br />
            　while (var &lt; MaxValue)<br />
            　{<br />
            　　//线程处理<br />
            　　::InterlockedIncrement(long*) &amp;var);<br />
            　}<br />
            　return 0;<br />
            }<br />
            请看下列程序：<br />
            int globalFlag = false; <br />
            DWORD WINAPI ThreadFunc(LPVOID n)<br />
            {<br />
            　Sleep(2000);<br />
            　globalFlag = true;<br />
            <br />
            　return 0;<br />
            }<br />
            <br />
            int main()<br />
            {<br />
            　HANDLE hThrd;<br />
            　DWORD threadId;<br />
            <br />
            　hThrd = CreateThread(NULL, 0, ThreadFunc, NULL, 0, &amp;threadId);<br />
            　if (hThrd)<br />
            　{<br />
            　　printf("Thread launched\n");<br />
            　　CloseHandle(hThrd);<br />
            　}<br />
            <br />
            　while (!globalFlag)<br />
            　;<br />
            　printf("exit\n");<br />
            }</td>
        </tr>
    </tbody>
</table>
<br />
　　上述程序中使用全局变量和while循环查询进行线程间同步，实际上，这是一种应该避免的方法，因为： <br />
<br />
　　（1）当主线程必须使自己与ThreadFunc函数的完成运行实现同步时，它并没有使自己进入睡眠状态。由于主线程没有进入睡眠状态，因此操作系统继续为它调度C P U时间，这就要占用其他线程的宝贵时间周期；<br />
<br />
　　（2）当主线程的优先级高于执行ThreadFunc函数的线程时，就会发生globalFlag永远不能被赋值为true的情况。因为在这种情况下，系统决不会将任何时间片分配给ThreadFunc线程。<br />
<br />
　　<strong>事件</strong><br />
<br />
　　事件(Event)是WIN32提供的最灵活的线程间同步方式，事件可以处于激发状态(signaled or true)或未激发状态(unsignal or false)。根据状态变迁方式的不同，事件可分为两类：<br />
<br />
　　（1）手动设置：这种对象只可能用程序手动设置，在需要该事件或者事件发生时，采用SetEvent及ResetEvent来进行设置。<br />
<br />
　　（2）自动恢复：一旦事件发生并被处理后，自动恢复到没有事件状态，不需要再次设置。<br />
<br />
　　创建事件的函数原型为：<br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>HANDLE CreateEvent(<br />
            　LPSECURITY_ATTRIBUTES lpEventAttributes,<br />
            　// SECURITY_ATTRIBUTES结构指针，可为NULL<br />
            　BOOL bManualReset, <br />
            　// 手动/自动<br />
            　// TRUE：在WaitForSingleObject后必须手动调用ResetEvent清除信号<br />
            　// FALSE：在WaitForSingleObject后，系统自动清除事件信号<br />
            　BOOL bInitialState, //初始状态<br />
            　LPCTSTR lpName //事件的名称<br />
            );</td>
        </tr>
    </tbody>
</table>
<br />
　　使用"事件"机制应注意以下事项：<br />
<br />
　　（1）如果跨进程访问事件，必须对事件命名，在对事件命名的时候，要注意不要与系统命名空间中的其它全局命名对象冲突；<br />
<br />
　　（2）事件是否要自动恢复；<br />
<br />
　　（3）事件的初始状态设置。<br />
<br />
　　由于event对象属于内核对象，故进程B可以调用OpenEvent函数通过对象的名字获得进程A中event对象的句柄，然后将这个句柄用于ResetEvent、SetEvent和WaitForMultipleObjects等函数中。此法可以实现一个进程的线程控制另一进程中线程的运行，例如： <br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>HANDLE hEvent=OpenEvent(EVENT_ALL_ACCESS,true,"MyEvent"); <br />
            ResetEvent(hEvent);</td>
        </tr>
    </tbody>
</table>
<br />
<strong>临界区<br />
<br />
</strong>　　定义临界区变量<br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>CRITICAL_SECTION gCriticalSection;</td>
        </tr>
    </tbody>
</table>
<br />
　　通常情况下，CRITICAL_SECTION结构体应该被定义为全局变量，以便于进程中的所有线程方便地按照变量名来引用该结构体。<br />
<br />
　　初始化临界区<br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>VOID WINAPI InitializeCriticalSection(<br />
            　LPCRITICAL_SECTION lpCriticalSection<br />
            　//指向程序员定义的CRITICAL_SECTION变量<br />
            );</td>
        </tr>
    </tbody>
</table>
<br />
　　该函数用于对pcs所指的CRITICAL_SECTION结构体进行初始化。该函数只是设置了一些成员变量，它的运行一般不会失败，因此它采用了VOID类型的返回值。该函数必须在任何线程调用EnterCriticalSection函数之前被调用，如果一个线程试图进入一个未初始化的CRTICAL_SECTION，那么结果将是很难预计的。<br />
<br />
　　删除临界区<br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>VOID WINAPI DeleteCriticalSection(<br />
            　LPCRITICAL_SECTION lpCriticalSection<br />
            　//指向一个不再需要的CRITICAL_SECTION变量<br />
            );</td>
        </tr>
    </tbody>
</table>
<br />
　　进入临界区<br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>VOID WINAPI EnterCriticalSection(<br />
            　LPCRITICAL_SECTION lpCriticalSection<br />
            　//指向一个你即将锁定的CRITICAL_SECTION变量<br />
            );</td>
        </tr>
    </tbody>
</table>
<br />
　　离开临界区<br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>VOID WINAPI LeaveCriticalSection(<br />
            　LPCRITICAL_SECTION lpCriticalSection<br />
            　//指向一个你即将离开的CRITICAL_SECTION变量<br />
            );</td>
        </tr>
    </tbody>
</table>
<br />
　　使用临界区编程的一般方法是：<br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>void UpdateData()<br />
            {<br />
            　EnterCriticalSection(&amp;gCriticalSection);<br />
            　...//do something<br />
            　LeaveCriticalSection(&amp;gCriticalSection);<br />
            }</td>
        </tr>
    </tbody>
</table>
<br />
　　关于临界区的使用，有下列注意点：<br />
<br />
　　（1）每个共享资源使用一个CRITICAL_SECTION变量；<br />
<br />
　　（2）不要长时间运行关键代码段，当一个关键代码段长时间运行时，其他线程就会进入等待状态，这会降低应用程序的运行性能；<br />
<br />
　　（3）如果需要同时访问多个资源，则可能连续调用EnterCriticalSection；<br />
<br />
　　（4）Critical Section不是OS核心对象，如果进入临界区的线程"挂"了，将无法释放临界资源。这个缺点在Mutex中得到了弥补。<br />
<br />
　　<strong>互斥</strong><br />
<br />
　　互斥量的作用是保证每次只能有一个线程获得互斥量而得以继续执行，使用CreateMutex函数创建： <br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>HANDLE CreateMutex(<br />
            　LPSECURITY_ATTRIBUTES lpMutexAttributes,<br />
            　// 安全属性结构指针，可为NULL<br />
            　BOOL bInitialOwner, <br />
            　//是否占有该互斥量，TRUE：占有，FALSE：不占有<br />
            　LPCTSTR lpName <br />
            　//信号量的名称<br />
            );<br />
            </td>
        </tr>
    </tbody>
</table>
<br />
　　Mutex是核心对象，可以跨进程访问，下面的代码给出了从另一进程访问命名Mutex的例子：<br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>HANDLE hMutex;<br />
            hMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, L"mutexName"); <br />
            if (hMutex){<br />
            　&#8230; <br />
            ｝<br />
            else{<br />
            　&#8230;<br />
            }</td>
        </tr>
    </tbody>
</table>
<br />
　　相关API：<br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>BOOL WINAPI ReleaseMutex(<br />
            　HANDLE hMutex<br />
            );</td>
        </tr>
    </tbody>
</table>
<br />
　　使用互斥编程的一般方法是：<br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>void UpdateResource()<br />
            {<br />
            　WaitForSingleObject(hMutex,&#8230;);<br />
            　...//do something<br />
            　ReleaseMutex(hMutex);<br />
            }</td>
        </tr>
    </tbody>
</table>
<br />
　　互斥(mutex)内核对象能够确保线程拥有对单个资源的互斥访问权。互斥对象的行为特性与临界区相同，但是互斥对象属于内核对象，而临界区则属于用户方式对象，因此这导致mutex与Critical Section的如下不同：<br />
<br />
　　（1） 互斥对象的运行速度比关键代码段要慢；<br />
<br />
　　（2） 不同进程中的多个线程能够访问单个互斥对象；<br />
<br />
　　（3） 线程在等待访问资源时可以设定一个超时值。<br />
<br />
　　下图更详细地列出了互斥与临界区的不同：<br />
<br />
<table width="90%" align="center" border="0">
    <tbody>
        <tr>
            <td>
            <div align="center"><img alt="" src="http://www.kuqin.com/upimg/allimg/070908/0049370.jpg" border="0" /></div>
            </td>
        </tr>
    </tbody>
</table>
<br />
<strong>信号量<br />
<br />
</strong>　　信号量是维护0到指定最大值之间的同步对象。信号量状态在其计数大于0时是有信号的，而其计数是0时是无信号的。信号量对象在控制上可以支持有限数量共享资源的访问。<br />
<br />
　　信号量的特点和用途可用下列几句话定义：<br />
<br />
　　（1）如果当前资源的数量大于0，则信号量有效；<br />
<br />
　　（2）如果当前资源数量是0，则信号量无效；<br />
<br />
　　（3）系统决不允许当前资源的数量为负值；<br />
<br />
　　（4）当前资源数量决不能大于最大资源数量。<br />
<br />
　　创建信号量<br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>HANDLE CreateSemaphore (<br />
            　PSECURITY_ATTRIBUTE psa,<br />
            　LONG lInitialCount, //开始时可供使用的资源数<br />
            　LONG lMaximumCount, //最大资源数<br />
            PCTSTR pszName);</td>
        </tr>
    </tbody>
</table>
<br />
　　释放信号量<br />
<br />
　　通过调用ReleaseSemaphore函数，线程就能够对信标的当前资源数量进行递增，该函数原型为：<br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>BOOL WINAPI ReleaseSemaphore(<br />
            　HANDLE hSemaphore,<br />
            　LONG lReleaseCount, //信号量的当前资源数增加lReleaseCount<br />
            　LPLONG lpPreviousCount<br />
            );</td>
        </tr>
    </tbody>
</table>
<br />
　　打开信号量<br />
<br />
　　和其他核心对象一样，信号量也可以通过名字跨进程访问，打开信号量的API为：<br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>HANDLE OpenSemaphore (<br />
            　DWORD fdwAccess,<br />
            　BOOL bInherithandle,<br />
            　PCTSTR pszName<br />
            );</td>
        </tr>
    </tbody>
</table>
<br />
　　<strong>互锁访问</strong><br />
<br />
　　当必须以原子操作方式来修改单个值时，互锁访问函数是相当有用的。所谓原子访问，是指线程在访问资源时能够确保所有其他线程都不在同一时间内访问相同的资源。<br />
<br />
　　请看下列代码：<br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>int globalVar = 0;<br />
            <br />
            DWORD WINAPI ThreadFunc1(LPVOID n)<br />
            {<br />
            　globalVar++;<br />
            　return 0;<br />
            }<br />
            DWORD WINAPI ThreadFunc2(LPVOID n)<br />
            {<br />
            　globalVar++;<br />
            　return 0;<br />
            }</td>
        </tr>
    </tbody>
</table>
<br />
　　运行ThreadFunc1和ThreadFunc2线程，结果是不可预料的，因为globalVar++并不对应着一条机器指令，我们看看globalVar++的反汇编代码：<br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>00401038 mov eax,[globalVar (0042d3f0)]<br />
            0040103D add eax,1<br />
            00401040 mov [globalVar (0042d3f0)],eax</td>
        </tr>
    </tbody>
</table>
<br />
　　在"mov eax,[globalVar (0042d3f0)]" 指令与"add eax,1" 指令以及"add eax,1" 指令与"mov [globalVar (0042d3f0)],eax"指令之间都可能发生线程切换，使得程序的执行后globalVar的结果不能确定。我们可以使用InterlockedExchangeAdd函数解决这个问题：<br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>int globalVar = 0;<br />
            <br />
            DWORD WINAPI ThreadFunc1(LPVOID n)<br />
            {<br />
            　InterlockedExchangeAdd(&amp;globalVar,1);<br />
            　return 0;<br />
            }<br />
            DWORD WINAPI ThreadFunc2(LPVOID n)<br />
            {<br />
            　InterlockedExchangeAdd(&amp;globalVar,1);<br />
            　return 0;<br />
            }</td>
        </tr>
    </tbody>
</table>
<br />
　　InterlockedExchangeAdd保证对变量globalVar的访问具有"原子性"。互锁访问的控制速度非常快，调用一个互锁函数的CPU周期通常小于50，不需要进行用户方式与内核方式的切换（该切换通常需要运行1000个CPU周期）。<br />
<br />
　　互锁访问函数的缺点在于其只能对单一变量进行原子访问，如果要访问的资源比较复杂，仍要使用临界区或互斥。<br />
<br />
　　可等待定时器<br />
<br />
　　可等待定时器是在某个时间或按规定的间隔时间发出自己的信号通知的内核对象。它们通常用来在某个时间执行某个操作。<br />
<br />
　　创建可等待定时器<br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>HANDLE CreateWaitableTimer(<br />
            　PSECURITY_ATTRISUTES psa,<br />
            　BOOL fManualReset,//人工重置或自动重置定时器<br />
            PCTSTR pszName);</td>
        </tr>
    </tbody>
</table>
<br />
　　设置可等待定时器<br />
<br />
　　可等待定时器对象在非激活状态下被创建，程序员应调用 SetWaitableTimer函数来界定定时器在何时被激活：<br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>BOOL SetWaitableTimer(<br />
            　HANDLE hTimer, //要设置的定时器<br />
            　const LARGE_INTEGER *pDueTime, //指明定时器第一次激活的时间<br />
            　LONG lPeriod, //指明此后定时器应该间隔多长时间激活一次<br />
            　PTIMERAPCROUTINE pfnCompletionRoutine,<br />
            　PVOID PvArgToCompletionRoutine,<br />
            BOOL fResume);</td>
        </tr>
    </tbody>
</table>
<br />
　　取消可等待定时器<br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>BOOl Cancel WaitableTimer(<br />
            　HANDLE hTimer //要取消的定时器<br />
            );</td>
        </tr>
    </tbody>
</table>
<br />
　　打开可等待定时器<br />
<br />
　　作为一种内核对象，WaitableTimer也可以被其他进程以名字打开：<br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>HANDLE OpenWaitableTimer (<br />
            　DWORD fdwAccess,<br />
            　BOOL bInherithandle,<br />
            　PCTSTR pszName<br />
            );</td>
        </tr>
    </tbody>
</table>
<br />
　　<strong>实例</strong><br />
<br />
　　下面给出的一个程序可能发生死锁现象：<br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>#include &lt;windows.h&gt;<br />
            #include &lt;stdio.h&gt;<br />
            CRITICAL_SECTION cs1, cs2;<br />
            long WINAPI ThreadFn(long);<br />
            main()<br />
            {<br />
            　long iThreadID;<br />
            　InitializeCriticalSection(&amp;cs1);<br />
            　InitializeCriticalSection(&amp;cs2);<br />
            　CloseHandle(CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadFn, NULL, 0,&amp;iThreadID));<br />
            　while (TRUE)<br />
            　{<br />
            　　EnterCriticalSection(&amp;cs1);<br />
            　　printf("\n线程1占用临界区1");<br />
            　　EnterCriticalSection(&amp;cs2);<br />
            　　printf("\n线程1占用临界区2");<br />
            <br />
            　　printf("\n线程1占用两个临界区");<br />
            <br />
            　　LeaveCriticalSection(&amp;cs2);<br />
            　　LeaveCriticalSection(&amp;cs1);<br />
            <br />
            　　printf("\n线程1释放两个临界区");<br />
            　　Sleep(20);<br />
            　};<br />
            　return (0);<br />
            }<br />
            <br />
            long WINAPI ThreadFn(long lParam)<br />
            {<br />
            　while (TRUE)<br />
            　{<br />
            　　EnterCriticalSection(&amp;cs2);<br />
            　　printf("\n线程2占用临界区2");<br />
            　　EnterCriticalSection(&amp;cs1);<br />
            　　printf("\n线程2占用临界区1");<br />
            <br />
            　　printf("\n线程2占用两个临界区");<br />
            <br />
            　　LeaveCriticalSection(&amp;cs1);<br />
            　　LeaveCriticalSection(&amp;cs2);<br />
            <br />
            　　printf("\n线程2释放两个临界区");<br />
            　　Sleep(20);<br />
            　};<br />
            }</td>
        </tr>
    </tbody>
</table>
<br />
　　运行这个程序，在中途一旦发生这样的输出：<br />
<br />
　　线程1占用临界区1<br />
<br />
　　线程2占用临界区2<br />
<br />
　　或<br />
<br />
　　线程2占用临界区2<br />
<br />
　　线程1占用临界区1<br />
<br />
　　或<br />
<br />
　　线程1占用临界区2<br />
<br />
　　线程2占用临界区1<br />
<br />
　　或<br />
<br />
　　线程2占用临界区1<br />
<br />
　　线程1占用临界区2<br />
<br />
　　程序就"死"掉了，再也运行不下去。因为这样的输出，意味着两个线程相互等待对方释放临界区，也即出现了死锁。<br />
<br />
　　如果我们将线程2的控制函数改为：<br />
<br />
<table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1">
    <tbody>
        <tr>
            <td>long WINAPI ThreadFn(long lParam)<br />
            {<br />
            　while (TRUE)<br />
            　{<br />
            　　EnterCriticalSection(&amp;cs1);<br />
            　　printf("\n线程2占用临界区1");<br />
            　　EnterCriticalSection(&amp;cs2);<br />
            　　printf("\n线程2占用临界区2");<br />
            <br />
            　　printf("\n线程2占用两个临界区");<br />
            <br />
            　　LeaveCriticalSection(&amp;cs1);<br />
            　　LeaveCriticalSection(&amp;cs2);<br />
            <br />
            　　printf("\n线程2释放两个临界区");<br />
            　　Sleep(20);<br />
            　};<br />
            }</td>
        </tr>
    </tbody>
</table>
<br />
　　再次运行程序，死锁被消除，程序不再挡掉。这是因为我们改变了线程2中获得临界区1、2的顺序，消除了线程1、2相互等待资源的可能性。<br />
<br />
　　由此我们得出结论，在使用线程间的同步机制时，要特别留心死锁的发生。<br />
<script type="text/javascript"><!-- google_ad_client="pub-9317413389774415" ;
/* 底部广告336x280, 创建于 08-7-23 */ google_ad_slot="9618407928" ; google_ad_width="336;
google_ad_height" = 280;
//-->
</script><script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script><script>window.google_render_ad();</script>
 <img src ="http://www.blogjava.net/tinysun/aggbug/243521.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2008-11-30 13:25 <a href="http://www.blogjava.net/tinysun/archive/2008/11/30/243521.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>