WaitForSingleObject的详细用法

用户模式的线程同步机制效率高,如果需要考虑线程同步问题,应该首先考虑用户模式的线程同步方法。
  但是,用户模式的线程同步有限制,对于多个进程之间的线程同步,用户模式的线程同步方法无能为力。这时,只能考虑使用内核模式。

  Windows提供了许多内核对象来实现线程的同步。对于线程同步而言,这些内核对象有两个非常重要的状态:“已通知”状态,“未通知”状态(也有翻译为:受信状态,未受信状态)。Windows提供了几种内核对象可以处于已通知状态和未通知状态:进程、线程、作业、文件、控制台输入/输出/错误流、事件、等待定时器、信号量、互斥对象。

  你可以通知一个内核对象,使之处于“已通知状态”,然后让其他等待在该内核对象上的线程继续执行。你可以使用Windows提供的API函数,等待函数来等待某一个或某些内核对象变为已通知状态。

  你可以使用WaitForSingleObject函数来等待一个内核对象变为已通知状态:

DWORD WaitForSingleObject(
HANDLE hObject, //指明一个内核对象的句柄
DWORD dwMilliseconds); //等待时间
  该函数需要传递一个内核对象句柄,该句柄标识一个内核对象,如果该内核对象处于未通知状态,则该函数导致线程进入阻塞状态;如果该内核对象处于已通知状态,则该函数立即返回WAIT_OBJECT_0。第二个参数指明了需要等待的时间(毫秒),可以传递INFINITE指明要无限期等待下去,如果第二个参数为0,那么函数就测试同步对象的状态并立即返回。如果等待超时,该函数返回WAIT_TIMEOUT。如果该函数失败,返回WAIT_FAILED。可以通过下面的代码来判断:

DWORD dw = WaitForSingleObject(hProcess, 5000); //等待一个进程结束
switch (dw)
{
case WAIT_OBJECT_0:
// hProcess所代表的进程在5秒内结束
break;

case WAIT_TIMEOUT:
// 等待时间超过5秒
break;

case WAIT_FAILED:
// 函数调用失败,比如传递了一个无效的句柄
break;
}
  
还可以使用WaitForMulitpleObjects函数来等待多个内核对象变为已通知状态:

DWORD WaitForMultipleObjects(
DWORD dwCount, //等待的内核对象个数
CONST HANDLE* phObjects, //一个存放被等待的内核对象句柄的数组
BOOL bWaitAll, //是否等到所有内核对象为已通知状态后才返回
DWORD dwMilliseconds); //等待时间
  该函数的第一个参数指明等待的内核对象的个数,可以是0到MAXIMUM_WAIT_OBJECTS(64)中的一个值。phObjects参数是一个存放等待的内核对象句柄的数组。bWaitAll参数如果为TRUE,则只有当等待的所有内核对象为已通知状态时函数才返回,如果为FALSE,则只要一个内核对象为已通知状态,则该函数返回。第四个参数和WaitForSingleObject中的dwMilliseconds参数类似。

  该函数失败,返回WAIT_FAILED;如果超时,返回WAIT_TIMEOUT;如果bWaitAll参数为TRUE,函数成功则返回WAIT_OBJECT_0,如果bWaitAll为FALSE,函数成功则返回值指明是哪个内核对象收到通知。

  可以如下使用该函数:

HANDLE h[3]; //句柄数组

//三个进程句柄
h[0] = hProcess1;
h[1] = hProcess2;
h[2] = hProcess3;

DWORD dw = WaitForMultipleObjects(3, h, FALSE, 5000); //等待3个进程结束

switch (dw)
{
case WAIT_FAILED:
// 函数呼叫失败
break;

case WAIT_TIMEOUT:
// 超时
break;

case WAIT_OBJECT_0 + 0:
// h[0](hProcess1)所代表的进程结束
break;

case WAIT_OBJECT_0 + 1:
// h[1](hProcess2)所代表的进程结束
break;

case WAIT_OBJECT_0 + 2:
// h[2](hProcess3)所代表的进程结束
break;
}
  
你也可以同时通知一个内核对象,同时等待另一个内核对象,这两个操作以原子的方式进行:

DWORD SignalObjectAndWait(
HANDLE hObjectToSignal, //通知的内核对象
HANDLE hObjectToWaitOn, //等待的内核对象
DWORD dwMilliseconds, //等待的时间
BOOL bAlertable); //与IO完成端口有关的参数,暂不讨论
  该函数在内部使得hObjectToSignal参数所指明的内核对象变成已通知状态,同时等待hObjectToWaitOn参数所代表的内核对象。dwMilliseconds参数的用法与WaitForSingleObject函数类似。
  该函数返回如下:WAIT_OBJECT_0,WAIT_TIMEOUT,WAIT_FAILED,WAIT_IO_COMPLETION。

  等你需要通知一个互斥内核对象并等待一个事件内核对象的时候,可以这么写:

ReleaseMutex(hMutex);
WaitForSingleObject(hEvent, INFINITE);
  可是,这样的代码不是以原子的方式来操纵这两个内核对象。因此,可以更改如下:

SignalObjectAndWait(hMutex, hEvent, INFINITE, FALSE);

 

转自http://hi.baidu.com/shenlang277/blog/item/9daa36d3378b4c0f3bf3cf43.html

  • 12
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
《操作系统原理》实验指导书 实验一 生产者-消费者模型模拟进程调度 一、实验任务 1、在WINDOWS 2000环境下,创建一个控制台进程,此进程包括4个线程:2个生产者线程和2个消费者线程。 2、用信号量机制解决进程(线程)的同步与互斥问题。 二、实验目的 1.掌握基本的同步互斥算法,理解生产者和消费者模型。 2.了解Windows 2000/XP中多线程的并发执行机制,线程间的同步和互斥。 3.学习使用Windows 2000/XP中基本的同步对象,掌握相应的API。 三、实验要求 1.生产者消费者对缓冲区进行互斥操作。 2.缓冲区大小为10,缓冲区满则不允许生产者生产数据,缓冲区空则不允许消费者消费数据。 3.生产者消费者各循环操作50次。 四、设计思路和采取的方案 1.利用windows提供的API函数CreateSemaphore()创建信号量对象; CreateThread()创建线程; WaitForSingleObject()执行P操作; ReleaseSemaphore()执行V操作; WaitForMultipleObjects()主进程等待线程的结束等函数进行设计。 2.在Windows中,常见的同步对象有:信号量(Semaphore)、互斥量(Mutex) 。 使用这些对象都分为三个步骤,一是创建或者初始化; 接着请求该同步对象,随即进入临界区,这一步对应于互斥量的上锁;最后释放该同步对象,这对应于互斥量的解锁。这些同步对象在主进程中创建,在其子线程中都可。 实验二 存储管理 一、目的和要求 1. 实验目的 (1)掌握时间片轮换的进程调度算法; (2)掌握带优先级的进程调度算法; (3)选用面向对象的编程方法。 2、实验学时:2学时 3、实验要求 (1)自定义PCB的数据结构; (2)使用带优先级的时间片轮转法调度进程,每运行一个时间片,优先级减半。 (3)命令集 A)create 随机创建进程,进程的优先级与所需要的时间片随机决定; B)ps 查看当前进程状态 C)sleep 命令将进程挂起 D)kill 命令杀死进程 E)quit命令退出 二、实验内容 根据教师指定的实验课题,完成设计、编码、测试工作。 实验三 虚拟存储器 一、目的和要求 1. 实验目的 (1)掌握先进先出页面置换算法; (2)掌握随机替换页面置换算法; (3)掌握OPT页面置换算法; (4)掌握最近最少使用页面置换算法; (5)熟悉抖动现象及其产生原理; (6)熟悉C/C++编程。 2、实验学时:2学时 3、实验要求 (1)进程占用内存空间共640K,页面大小是1K/2K/4K/8K; (2)随机生成256个页面置换次序; (3)用于分配页面大小的内存总空间是32K; (4)给出四种页面置换算法的换页过程,并计算各自的缺页率。 二、实验内容 编写程序,使用四种不同的页面替换策略算法进行页面替换。分别是先进先出,随机替换,时钟页面替换,最近最久未使用页面替换,并计算缺页率。
您查询的关键词是:delphi 同步 数据 。如果打开速度慢,可以尝试快速版;如果想保存快照,可以添加到搜藏。 (百度和网页http://blog.csdn.net/mygodsos/archive/2008/10/19/3097921.aspx的作者无关,不对其内容负责。百度快照谨为网络故障时之索引,不代表被搜索网站的即时页面。) -------------------------------------------------------------------------------- 发呆茶馆 登录 注册 欢迎 退出 我的博客 配置 写文章 文章管理 博客首页 全站 当前博客 空间 博客 好友 相册 留言 用户操作 [发私信] [加为好友] mygodsos 订阅我的博客 mygodsos的公告 文章分类 Delphi Delphi学习--多线程 Delphi学习--自创的常用函数 期货大事记 生活感悟 投资理财 编程学习 万一的Delphi博客 存档 2009年05月(3) 2008年11月(13) 2008年10月(8) 2008年09月(3) ◆Delphi多线程编程之三 同步读写全局数据 ◆(乌龙哈里2008-10-12) 收藏 ◆Delphi多线程编程之三同步读写全局数据 ◆(乌龙哈里2008-10-12) (调试环境:Delphi2007+WinXPsp3 例程:Tst_Thread3.dpr) 开始研究最重要的多线程读写全局数据了,结合书上的例子,我修改成下面的情况: unit Tst_Thread3U; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Memo1: TMemo; Button2: TButton; Button3: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); private procedure ThreadsDone(Sender: TObject); end; TMyThread=class(TThread) protected procedure Execute;override; end; var Form1: TForm1; implementation {$R *.dfm} const MaxSize=128; var NextNumber:Integer=0; DoneFlags:Integer=0; GlobalArry:array[1..MaxSize] of Integer; Lock:byte; //1-不同步 2-临界区 3-互斥 CS:TRTLCriticalSection; //临界区 hMutex:THandle; //互斥 function GetNextNumber:Integer; begin Result:=NextNumber; inc(NextNumber); end; procedure TMyThread.Execute; var i:Integer; begin FreeOnTerminate:=True; //终止后自动free OnTerminate:=Form1.ThreadsDone; if Lock3 then //非互斥情况 begin if Lock=2 then EnterCriticalSection(CS); //建立临界区 for i := 1 to MaxSize do begin GlobalArry[i]:=GetNextNumber; Sleep(5); end; if Lock=2 then LeaveCriticalSection(CS);//离开临界区 end else //-------互斥 begin if WaitForSingleObject(hMutex,INFINITE)=WAIT_OBJECT_0 then begin for i := 1 to MaxSize do begin GlobalArry[i]:=GetNextNumber; Sleep(5); end; end; ReleaseMutex(hMutex); //释放 end; end; procedure TForm1.ThreadsDone(Sender: TObject); var i:Integer; begin Inc(DoneFlags); if DoneFlags=2 then begin for i := 1 to MaxSize do Memo1.Lines.Add(inttostr(GlobalArry[i])); if Lock=2 then DeleteCriticalSection(CS); //删除临界区 If Lock=3 then CloseHandle(hMutex); //关闭互斥 end; end; //非同步 procedure TForm1.Button1Click(Sender: TObject); begin Lock:=1; TMyThread.Create(False); TMyThread.Create(False); end; //临界区 procedure TForm1.Button2Click(Sender: TObject); begin Lock:=2; InitializeCriticalSection(CS); //初始化临界区 TMyThread.Create(False); TMyThread.Create(False); end; //互斥 procedure TForm1.Button3Click(Sender: TObject); begin Lock:=3; // 互斥 hMutex:=CreateMutex(0,False,nil); TMyThread.Create(False); TMyThread.Create(False); end; end. 没有临界区和互斥的帮助,两个线程都不断地在Memo1输出,而且数字是乱的。 一、临界区 所谓临界区,就是一次只能由一个线程来执行的一段代码。如果把初始化数组的代码放在临界区内,另一个线程在第一个线程处理完之前是不会被执行的。 使用临界区的步骤: 1、先声明一个全局变量类型为TRTLCriticalSection; 2、在线程Create()前调用InitializeCriticalSection()过程来初始化,该函数定义是: void WINAPI InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 类型lpCriticalSection即是Delphi封装的TRTLCriticalSection。 3、在线程的需要放入临界区的代码前面使用EnterCriticalSection(lpCriticalSection)过程来开始建立临界区。在代码完成后用LeaveCriticalSection(lpCriticalSection)来标志临界区的结束。 4、在线程执行完后用DeleteCriticalSection(lpCriticalSection)来清除临界区。这个清除过程必须放在线程执行完后的地方,比如FormDesroy事件中。上面的例子中,若把该过程放在TMyThread.Create(False);后,会产生错误。 二、互斥: 互斥非常类似于临界区,除了两个关键的区别:首先,互斥可用于跨进程的线程同步。其次,互斥能被赋予一个字符串名字,并且通过引用此名字创建现有互斥对象的附加句柄。 提示临界区与事件对象(比如互斥对象)的最大的区别是在性能上。临界区在没有线程冲突时,要用10~15个时间片,而事件对象由于涉及到系统内核要用400~600个时间片。 使用互斥的步骤: 1、声明一个类型为Thandle或Hwnd的全局变量,其实都是Cardinal类型。Hwnd是handle of window,主要用于窗口句柄;而Thandle则没有限制。 2、线程Create()前用CreateMutex()来创建一个互斥量。该函数定义为: HANDLE WINAPI CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName:Pchar); LPSECURITY_ATTRIBUTES参数为一个指向TSecurityAttributtes记录的指针。此参数设为nil,表示访问控制列表默认的安全属性。 bInitalOwner参数表示创建互斥对象的线程是否要成为此互斥对象的拥有者。当此参数为False时,表示互斥对象没有拥有者。 lpName参数指定互斥对象的名称。设为nil表示无命名,如果参数不是设为nil,函数会搜索是否有同名的互斥对象存在。如果有,函数就会返回同名互斥对象的句柄。否则,就新创建一个互斥对象并返回其句柄。 返回值是一handle。当错误发生时,返回null,此时用GetLastError函数可查看错误的信息。 利用CreateMutex()可以防止程序多个实例运行,如下例: Program ABC; Uses Forms,Windows,…; {$R *.res} Var hMutex:Hwnd; Begin Application.Initialize; hMutex:=CreateMutex(nil,False,Pchar(Application.Title)); if GetLastErrorERROR_ALREADY_EXISTS then begin //项目要运行的咚咚 end; ReleaseMutex(hMutex); Application.Run; End; 在本节的例程中,我们只是要防止线程进入同步代码区域中,所以lpName参数设置为nil。 3、在同步代码前用WaitForSingleObject()函数。该函数使得线程取得互斥对象(同步代码)的拥有权。该函数定义为: DWORD WINAPI WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds); 这个函数可以使当前线程在dwMilliseconds指定的时间内睡眠,直到hHandle参数指定的对象进入发信号状态为止。一个互斥对象不再被线程拥有时,它就进入发信号状态。当一个进程要终止时,它就进入发信号状态。dwMilliseconds参数可以设为0,这意味着只检查hHandle参数指定的对象是否处于发信号状态,而后立即返回。dwMilliseconds参数设为INFINITE,表示如果信号不出现将一直等下去。 这个函数的返回值含义: WAIT_ABANDONED 指定的对象是互斥对象,并且拥有这个互斥对象的线程在没有释放此对象之前就已终止。此时就称互斥对象被抛弃。这种情况下,这个互斥对象归当前线程所有,并把它设为非发信号状态 WAIT_OBJECT_0 指定的对象处于发信号状态 WAIT_TIMEOUT 等待的时间已过,对象仍然是非发信号状态 再次声明,当一个互斥对象不再被一个线程所拥有,它就处于发信号状态。此时首先调用WaitForSingleObject()函数的线程就成为该互斥对象的拥有者,此互斥对象设为不发信号状态。当线程调用ReleaseMutex()函数并传递一个互斥对象的句柄作为参数时,这种拥有关系就被解除,互斥对象重新进入发信号状态。 注意除WaitForSingleObject()函数外,你还可以使用WaitForMultipleObject()和MsgWaitForMultipleObject()函数,它们可以等待几个对象变为发信号状态。这两个函数的详细情况请看Win32 API联机文档。 4、在同步代码结束后,使用ReleaseMutex(THandle)函数来标志。该函数只是释放互斥对象和线程的拥有者关系,并不释放互斥对象的句柄。 5、调用CloseHandle(THandle)来关闭互斥对象。请注意例程中该函数的使用位置。 三、还有一种用信号量对象来管理线程同步的,它是在互斥的基础上建立的,但信号量增加了资源计数的功能,预定数目的线程允许同时进入要同步的代码。有点复杂,想不到在哪可以用,现在就不研究论了。 发表于 @ 2008年10月19日 00:47:00 | 评论( loading... ) | 编辑| 举报| 收藏 旧一篇:◆delphi多线程编程之二 ◆(乌龙哈里2008-10-12) | 新一篇:◆Delphi多线程编程之四 线程安全和VCL ◆(乌龙哈里2008-10-12)Csdn Blog version 3.1a Copyright © mygodsos
::WaitForSingleObject是Windows API中的一个函数,用于等待一个内核对象的信号状态,直到等待时间结束或者对象信号状态发生变化。其用法如下: 1.等待一个线程的结束: ```c++ ::WaitForSingleObject(hThread, INFINITE); ``` 其中hThread是一个线程句柄,INFINITE表示无限等待,直到线程结束。 2.等待一个事件的触发: ```c++ DWORD dwRet = ::WaitForSingleObject(hEvent, dwTimeout); if(dwRet == WAIT_OBJECT_0) { // 事件已经被触发 } else if(dwRet == WAIT_TIMEOUT) { // 等待超时 } else if(dwRet == WAIT_FAILED) { // 等待失败,可以通过GetLastError()获取错误码 } ``` 其中hEvent是一个事件句柄,dwTimeout是等待超时时间(单位为毫秒),返回值为WAIT_OBJECT_0表示事件已经被触发,WAIT_TIMEOUT表示等待超时,WAIT_FAILED表示等待失败。 3.等待多个对象中的任意一个对象的信号状态: ```c++ DWORD dwRet = ::WaitForMultipleObjects(nCount, hObjects, bWaitAll, dwTimeout); if(dwRet >= WAIT_OBJECT_0 && dwRet < WAIT_OBJECT_0 + nCount) { // 返回的是哪个对象触发了信号状态变化 } else if(dwRet == WAIT_TIMEOUT) { // 等待超时 } else if(dwRet == WAIT_FAILED) { // 等待失败,可以通过GetLastError()获取错误码 } ``` 其中nCount表示对象数量,hObjects是一个对象句柄数组,bWaitAll表示是否等待所有对象的信号状态变化,dwTimeout表示等待超时时间。返回值为WAIT_OBJECT_0到WAIT_OBJECT_0+nCount-1之间的值,表示哪个对象触发了信号状态变化;WAIT_TIMEOUT表示等待超时;WAIT_FAILED表示等待失败,可以通过GetLastError()获取错误码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值