WaitForSingleObject函数详解

Win 32 多线程程序设计学习笔记之三:*WaitFor*Object函数 1.等待一个线程的结束 DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds ); 参数 hHandle: 等待对象的 handle(代表一个核心对象)。在本例中,此为线程 handle。 dwMilliseconds :等待的最长时间。时间终了,即使 handle 尚未成为激发状态,此函数还是要返回。此值可以是 0 阅读详情
AI摘要

本文详细解析了WaitForSingleObject函数,包括其核心概念、函数原型、参数详解、返回值以及工作流程。通过代码示例和错误处理分析,阐述了如何在不同情况下使用该函数进行线程同步,并强调了检查返回值的重要性。

WaitForSingleObject函数详解

核心概念

WaitForSingleObject 是一个用于线程同步的核心函数。它的作用很简单:让一个线程等待,直到某个特定的“对象”达到“已通知”的状态,或者超过指定的等待时间。

你可以把它想象成:

  • 你在等一个朋友(对象)打电话(变为已通知状态)给你。
  • 你决定最多等30分钟(超时时间)。
  • 结果有两种:要么他在30分钟内打来了(对象已通知),要么30分钟到了他还没打(超时)。

函数原型

DWORD WaitForSingleObject(
  [in] HANDLE hHandle,        // 要等待的对象的句柄
  [in] DWORD  dwMilliseconds  // 超时时间,以毫秒为单位
);

参数详解

  1. hHandle [输入参数]

    • 这是你要等待的内核对象的句柄。
    • 这个对象必须是一个可以“被等待”的同步对象。常见的包括:
      • 线程 (Thread): 等待线程执行结束。线程结束时变为已通知状态。
      • 进程 (Process): 等待进程执行结束。进程结束时变为已通知状态。
      • 互斥量 (Mutex): 等待获取该互斥量的所有权。当其他线程释放互斥量时,它变为已通知状态。
      • 事件 (Event): 等待事件被“触发”。另一个线程可以调用 SetEvent 来手动将其设置为已通知状态。
      • 信号量 (Semaphore): 等待信号量的计数大于0。当计数大于0时,它为已通知状态。
  2. dwMilliseconds [输入参数]

    • 超时时间,单位是毫秒(milliseconds)。
    • 如果这个参数设置为 0,函数会立即测试对象的状态并立即返回,而不会真正地阻塞线程。
    • 如果这个参数设置为 INFINITE(一个定义为0xFFFFFFFF的常量),函数会无限期地等待,直到对象变为已通知状态。这是一个阻塞调用,需要谨慎使用,以免造成程序死锁。

返回值

函数的返回值告诉你等待的结果,它是一个 DWORD 类型的值。

  • WAIT_OBJECT_0 (值为 0)

    • 等待成功! 这表示你等待的对象已经变成了“已通知”状态。你的线程现在可以继续执行后续任务了(例如,获取互斥锁、处理线程结束后的资源清理等)。
  • WAIT_TIMEOUT (值为 258)

    • 等待超时。 在指定的 dwMilliseconds 时间过后,对象仍然没有变成“已通知”状态。线程会从等待中唤醒并继续执行。
  • WAIT_ABANDONED (值为 0x80)

    • 这是一个特殊情况,主要针对互斥量 (Mutex)。 它表示你等待的互斥量被另一个线程在结束时遗弃了(即该线程在释放互斥量之前就终止了)。虽然你仍然能获取到这个互斥量,但可能意味着受保护的共享数据处于不确定状态,需要特别小心处理。
    • 对于其他对象,不会返回这个值。
  • WAIT_FAILED (值为 (DWORD)0xFFFFFFFF)

    • 函数调用失败。 例如,你传入了一个无效的句柄。此时可以调用 GetLastError() 来获取具体的错误信息。

工作流程图解

下图直观地展示了 WaitForSingleObject 的工作逻辑:

是
否
dwMilliseconds = 0
dwMilliseconds = INFINITE
设置了具体时间
是
是
否
调用 WaitForSingleObject
对象是否已 signaled?
立即返回 WAIT_OBJECT_0
设置超时时间?
立即返回 WAIT_TIMEOUT
无限期阻塞等待
阻塞等待指定时间
对象变为 signaled?
返回 WAIT_OBJECT_0
在时间内对象变为 signaled?
返回 WAIT_TIMEOUT

一个简单的代码示例 (C++)

以下示例演示了如何使用 WaitForSingleObject 来等待一个子线程结束。

#include <windows.h>
#include <iostream>

// 简单的线程函数
DWORD WINAPI MyThreadFunction(LPVOID lpParam) {
    std::cout << "子线程开始工作,模拟耗时3秒..." << std::endl;
    Sleep(3000); // 模拟工作,睡眠3秒
    std::cout << "子线程工作完成!" << std::endl;
    return 0;
}

int main() {
    // 创建一个子线程
    HANDLE hThread = CreateThread(
        NULL,                   // 默认安全属性
        0,                      // 默认栈大小
        MyThreadFunction,       // 线程函数
        NULL,                   // 传递给线程函数的参数
        0,                      // 默认创建标志
        NULL                    // 不需要获取线程ID
    );

    if (hThread == NULL) {
        std::cerr << "创建线程失败!" << std::endl;
        return 1;
    }

    std::cout << "主线程正在等待子线程结束,最多等5秒..." << std::endl;

    // 主线程在这里等待子线程结束,最多等待5000毫秒(5秒)
    DWORD dwWaitResult = WaitForSingleObject(hThread, 5000);

    // 根据返回值判断发生了什么
    switch (dwWaitResult) {
        case WAIT_OBJECT_0:
            std::cout << "成功:子线程已正常结束。" << std::endl;
            break;
        case WAIT_TIMEOUT:
            std::cout << "警告:等待超时,子线程仍在运行。" << std::endl;
            break;
        case WAIT_FAILED:
            std::cout << "错误:WaitForSingleObject 调用失败。" << std::endl;
            break;
        // 对于线程,不会返回 WAIT_ABANDONED
    }

    // 不要忘记关闭内核对象句柄
    CloseHandle(hThread);
    std::cout << "主程序退出。" << std::endl;
    return 0;
}

可能的输出:

  1. 子线程在5秒内结束:输出 "成功:子线程已正常结束。"
  2. 子线程运行超过5秒:输出 "警告:等待超时,子线程仍在运行。"

总结与要点

  • 目的:WaitForSingleObject 是用于线程同步,让一个线程暂停运行,等待某个条件(对象变为已通知)被满足。
  • 核心参数:一个对象句柄和一个超时时间。
  • 核心返回值:WAIT_OBJECT_0(成功),WAIT_TIMEOUT(超时),WAIT_ABANDONED(互斥量被遗弃)。
  • 常见用途:
    • 等待创建的线程或进程结束,以便进行资源清理。
    • 等待获取一个同步对象(如互斥量、信号量)的所有权,以实现对共享资源的互斥访问。
    • 等待一个事件被触发,以协调多个线程的执行顺序。
  • 重要提醒:始终检查返回值,并根据不同的返回值进行相应的处理。特别是使用 INFINITE 时要小心死锁风险。使用后记得用 CloseHandle 关闭句柄。
    好的,这是一个非常重要且常见的场景。我们来详细解释当第二个参数是 INFINITE 时,如何以及为何要判断返回值是否等于 WAIT_FAILED。

当第二个参数是 INFINITE 时,如何以及为何要判断返回值是否等于 WAIT_FAILED?

核心结论

即使你将超时时间设置为 INFINITE(无限等待),函数仍然有可能因为错误而立即返回 WAIT_FAILED。 INFINITE 只保证函数在“没有错误发生”的情况下会一直等待对象,但它不能防止其他错误导致函数失败。

因此,在任何情况下,包括使用 INFINITE 时,都必须检查返回值是否为 WAIT_FAILED,这是健壮编程的基本要求。


为什么 INFINITE 情况下还会失败?

INFINITE 参数的含义是:“如果一切正常,就一直等下去,直到对象变为已通知状态”。
但它无法避免以下几种会导致函数执行失败(返回 WAIT_FAILED) 的情况:

  1. 无效的句柄 (Most Common)

    • 你传入的 hHandle 不是一个有效的、可以等待的内核对象句柄。
    • 例如:句柄为 NULL、句柄已经用 CloseHandle 关闭了、或者句柄根本就不是一个同步对象(比如是一个文件句柄)。
  2. 权限不足

    • 当前线程没有足够的权限来等待这个对象。这在跨进程等待时可能发生。
  3. 意外的系统错误

    • 极少数情况下,系统内部发生错误,导致等待操作无法进行。

流程图:INFINITE 模式的执行逻辑

下图清晰地展示了即使设置为无限等待,也必须在开始时进行有效性检查,并最终处理失败情况:

否
是
对象变为已通知
等待过程中出现系统级错误
调用 WaitForSingleObject
hHandle, INFINITE
句柄有效且有权等待?
立即返回 WAIT_FAILED
进入无限期阻塞等待状态
等待期间发生什么?
返回 WAIT_OBJECT_0
返回 WAIT_FAILED
必须检查并处理错误
按成功处理

从流程图可以看出,WAIT_FAILED 可能在两个阶段返回:

  1. 初始检查阶段:参数无效,函数根本不会开始等待。
  2. 等待过程中:极其罕见的情况下系统出错。

正确的代码实践

你必须先检查是否失败,然后再去判断是否是成功的等待。

HANDLE hThread = CreateThread(...); // 获取一个需要等待的句柄

// 使用 INFINITE 等待
DWORD dwWaitResult = WaitForSingleObject(hThread, INFINITE);

// 第一优先:检查是否调用失败
if (dwWaitResult == WAIT_FAILED) {
    // 调用 GetLastError 获取详细的错误代码
    DWORD dwError = GetLastError();
    // 进行错误处理,例如打印日志、清理资源、安全退出等
    printf("WaitForSingleObject failed! Error code: %d\n", dwError);
    CloseHandle(hThread);
    return -1;
}

// 第二优先:检查是否成功等待(只有在没有失败的情况下才做这个判断)
if (dwWaitResult == WAIT_OBJECT_0) {
    // 等待成功,对象已通知
    printf("Thread exited successfully.\n");
}
// 注意:因为参数是INFINITE,所以绝对不会返回WAIT_TIMEOUT
// 对于线程句柄,也绝对不会返回WAIT_ABANDONED

// ... 其他处理 ...
CloseHandle(hThread);

一个具体的错误场景示例

HANDLE hInvalidHandle = (HANDLE)12345; // 这是一个完全瞎编的无效句柄

// 即使使用 INFINITE,这个调用也会立即返回 WAIT_FAILED
// 因为它无法等待一个不存在的对象
DWORD dwResult = WaitForSingleObject(hInvalidHandle, INFINITE);

if (dwResult == WAIT_FAILED) {
    DWORD err = GetLastError();
    // 这里 err 很可能是 6 (ERROR_INVALID_HANDLE)
    printf("Failed as expected. Error: %d\n", err);
}

总结

  1. INFINITE 不代表“永不返回”:它只代表“在无错误情况下,为等待一个有效的对象而永不返回”。
  2. 错误处理优先:返回值检查的顺序应该是 先 WAIT_FAILED,再 WAIT_OBJECT_0,最后是其他状态。
  3. 必须检查 WAIT_FAILED:忽略这个检查意味着你的代码无法处理无效输入或意外情况,会导致程序在发生错误时行为不可预测(甚至崩溃)。
  4. 使用 GetLastError():一旦确认返回 WAIT_FAILED,立即调用 GetLastError() 来诊断具体原因,这是调试和错误处理的关键步骤。

所以,无论第二个参数是什么,判断是否等于 WAIT_FAILED 都是必不可少的一步。

C++程序中 WaitForSingleObject 函数的诸多用途与使用场景总结 本文详细介绍WaitForSingleObject函数在多线程编程中的多种使用场景,包括在多线程之间实现锁的等待、线程中实现Sleep、监测线程或进程是否退出等。 阅读详情

相关推荐

Windows C++多线程:使用WaitForSingleObject与WaitForMultipleObjects进行线程等待

在上面的示例中,首先使用CreateThread函数创建一个线程,并获取线程的句柄。其中,nCount参数是要等待的内核对象的数量,lpHandles参数是一个指向内核对象句柄数组的指针,bWaitAll参数指定是否等待所有对象(TRUE表示等待所有对象,FALSE表示等待任意一个对象),dwMilliseconds参数是等待的超时时间(以毫秒为单位)。其中,hHandle参数是要等待的内核对象的句柄(例如线程句柄、事件句柄等),dwMilliseconds参数是等待的超时时间(以毫秒为单位)。

2301_79325339的博客 666

《Windows via C/C++》学习笔记 —— 内核对象的“线程同步”之“等待函数”

  用户模式的线程同步机制效率高,如果需要考虑线程同步问题,应该首先考虑用户模式的线程同步方法。   但是,用户模式的线程同步有限制,对于多个进程之间的线程同步,用户模式的线程同步方法无能为力。这时,只能考虑使用内核模式。   Windows提供了许多内核对象来实现线程的同步。对于线程同步而言,这些内核对象有两个非常重要的状态:“已通知”状态,“未通知”状态(也有翻译为:受信状态,未受信...

weixin_30300225的博客 2077

东方马达步进电机AZM66AK-HS100+AZD-KD调试经验记录

东方马达步进电机AZM66AK-HS100+AZD-KD调试经验记录(20170803YC) 本次总结主要实现的功能如下:用MEXE02软件在步进电机驱动器中设置好控制动作,每个控制动作有一个特定的编号。之后通过PC和驱动器之间的485通讯,发送相关指令,使步进电机按照一定编号的控制动作运转。 参考资料:东方马达步进电机AZM66AK-HS100+AZD-KD的产品手册,可在官网下载。 A....

dahailuoa6的博客 1万+

WaitForSingleObject等待函数

等待函数使一个线程自愿进入等待状态,直到指定的内核对象被触发为止。如果线程在调用一个等待函数的时候,相应的内核对象已经处于触发状态,那么线程是不会进入等待状态的。最常用是WaitForSingleObject。);参数1:用来标识要等待的内核对象,这个内核对象可以处于触发状态或未触发状态。参数2:用来指定线程最多愿意花多长的时间来等待对象被触发(超时时间,单位为毫秒)如果指定了一个非0值,则该函数将等待,直到该对象被触发或时间间隔过去。如果为0,如果对象没有触发,函数不会进入等待状态,它总是立即返回。...

希望能帮助大家,希望大家多多支持,你们的支持是我前进的动力。 5834

vivado生成及调用自定义IP的方法

开发环境:vivado 2018.2 1.将整个工程封装为自定义IP 将整个工程封装为自定义IP主要有以下步骤: 步骤1.选中工程中的top模块。点击Tools ------> Create and Package New IP,如图1所示 图1 步骤2.弹出界面后,点击next至图2界面,选择Package your current project,然后继续点击next 图2 步骤3.选择生成的IP路径,如图3所示。这个路径就是后续添加该IP的路径 图3 步骤4.继续点击next,然后选择fi

进击的阿日比 4726

[代码开源]基于DistFlow的IEEE33和IEEE69节点最优潮流模型(OPF)实现与性能分析

本文详细介绍了基于DistFlow的IEEE33和IEEE69节点最优潮流模型(OPF)的实现与性能分析。通过代码开源,展示了如何从零搭建电力系统优化模型,解决配电网中的发电成本最小化问题。文章涵盖数据准备、模型实现细节、性能优化建议及常见问题解答,为电力系统研究者提供实用参考。

weixin_29103191的博客 707

WaitForSingleObject函数

WaitForSingleObject 当指定的对象处于有信号状态或者等待时间结束的状态时,此函数返回。 DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds ); 参数: hHandle

993

反相器(inverter)和缓冲器(buffer)---附加与非门原理图

下面是对反相器(Inverter)和缓冲器(Buffer)从面积、驱动能力、时序等多个角度的详细对比,列出的是一般情况下的比较,具体情况可能会受到工艺节点、设计库和特定设计要求的影响。缓冲器(Buffer)是数字电路中常见的逻辑门之一,用于增强信号的驱动能力,而不改变信号的逻辑状态。如下图所示的CMOS反相器,由一个NMOS和PMOS组成,栅端gate相连作为输入端Vin,漏端相连作为输出端Vout,NMOS的源端接地,PMOS的源端接电源VDD.与反相器不同,缓冲器的输出与输入保持相同的逻辑状态。

hzmscut的博客 1万+

WaitForSingleObject 函数

未通知”和“已通知”。未通知状态:表示该对象所代表的事件尚未发生。已通知状态:表示该对象所代表的事件已经发生。的功能就是阻塞当前线程,直到指定的对象从“未通知”变为“已通知”。线程 (Thread)HANDLE): 线程结束时变为已通知状态。进程 (Process)HANDLE): 进程结束时变为已通知状态。事件 (Event)HANDLE): 核心的同步对象,由程序手动或自动设置其状态。互斥体 (Mutex)HANDLE): 当它不被任何线程占有时变为已通知状态。信号量 (Semaphore)

2401_83957781的博客 952

FPGA 学习系列(2):Verilog 语法基础

Verilog 由门级建模数据流建模行为建模门级建模(Gate Level Modeling):使用基本逻辑门描述电路,接近硬件物理结构。数据流建模(Data Flow Modeling):使用assign语句进行组合逻辑建模,类似数学方程式。行为建模(Behavioral Modeling):使用always代码块和if-elsecase等控制语句建模,抽象程度较高。Verilog 代码一般由模块(module)组成,每个模块描述一个独立的硬件功能。

Azperk的博客 987

Google 二次签名 导致 Facebook , Google 登录失败

google 授权登录

xq610928的博客 1957

WaitForSingleObject函数的使用

WaitForSingleObject当指定的对象处于有信号状态或者等待时间结束的状态时,此函数返回。DWORD WaitForSingleObject(  HANDLE hHandle,  DWORD dwMilliseconds);参数: hHandle:指定对象或事件的句柄; dwMilliseconds: 等待时间,以毫妙为单位,当超过等待时间时,此函数将返回。如果该参数设置为0,则该函数

懒人程序员的学习过程 2万+

跨 Agent 语义沟通机制构建:共享知识图谱与语义对齐方法

在具身智能与多智能体系统协同日益复杂化的背景下,跨 Agent 的语义沟通能力成为任务完成效率和系统鲁棒性提升的关键技术路径。本文系统分析了当前业界在共享知识图谱、语义嵌入对齐、语义意图建模与跨模态语义通信协议设计方面的实践经验,深入讲解如何在 Habitat-MARL 与 Isaac-MARL 等平台中,基于实体-关系结构、语义分布表示与任务上下文,构建具备理解、共享、协作能力的多 Agent 语义系统。文章以工程实战为主,覆盖图谱构建、语义嵌入同步、对齐模型部署、通信接口集成等多个核心模块,结合真实应用

努力分享一些人工智能、计算机视觉、影像等相关的知识干货! 1292

WaitForSingleObject函数用法

   等待函数可使线程自愿进入等待状态,直到一个特定的内核对象变为已通知状态为止。这些等待函数中最常用的是WaitForSingleObject:  DWORD WaitForSingleObject(HANDLE hObject, DWORD dwMilliseconds);当线程调用该函数时,第一个参数hObject标识一个能够支持被通知/未通知的内核对象。第二个参数dwMilli

David的专栏 5392

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

WaitForSingleObject函数使用

WaitForSingleObject当指定的对象处于有信号状态或者等待时间结束的状态时,此函数返回。DWORD WaitForSingleObject(  HANDLE hHandle,  DWORD dwMilliseconds);参数: hHandle:指定对象或事件的句柄; dwMilliseconds: 等待时间,以毫妙为单位,当超过等待时间时,此函数将返回。如果该参数设置为0,则该...

muzi_tao的专栏 245

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

常用的一些函数WaitForSingleObject

WaitForSingleObject的用法                                       WaitForSingleObject的用法DWORD WaitForSingleObject(  HANDLE hHandle,  DWORD dwMilliseconds);参数hHandle是一个事件的句柄,第二个参数dwMilliseconds是时间间隔。如果时

woshihur 439

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

WaitForSingleObject()函数

VC声明DWORD WaitForSingleObject(   HANDLE hHandle,   DWORD dwMilliseconds   ); 参数hHandle是一个事件的句柄,第二个参数dwMilliseconds是时间间隔。如果事件是有信号状态返回WAIT_OBJECT_0,如果时间超过dwMilliseconds值但时间事件还是无信号状态则返回WAIT_TIM

技术大白的博客 1万+

4.WaitForSingleObject函数分析

无论可等待对象是何种类型,线程都是通过: WaitForSingleObject WaitForMultipleObjects 进入等待状态的,这两个函数是理解线程等待与唤醒进制的核心 WaitForSingleObject参数说明 WaitForSingleObject对应的内核函数: NTSTATUS stdcall NtWaitForSingleObject ( HANDLE Handl...

My classmates 997
上一篇: 在C++中#pragma“可选预处理指令的作用“。
吴_知遇
博客等级 码龄7年 100粉丝 26原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值