【问题标题】:Can you really wait on Condition Variable with WaitFor...Object(s)?你真的可以用 WaitFor...Object(s) 等待条件变量吗?
【发布时间】:2016-05-30 09:25:04
【问题描述】:


我正在尝试对许多CONDITION_VARIABLE 实施某种等待。
The answers here 暗示 WaitForMultipleObjects 等是处理 Windows API(以及互联网上的更多地方)时的有效选项,但似乎并非如此。

首先,MSDN 文档中没有写到 Windows 条件变量是 WaitFor... 函数的有效参数。
其次,WaitFor... 似乎只接受HANDLE 类型作为参数,这基本上是一个内核对象。但PCONDITION_VARIABLE 并不是真正的HANDLE
最后,尝试使用条件变量(作为PCONDITION_VARIABLE 和未记录的CONDITION_VARIABLE::Ptr)使函数返回错误代码6(无效句柄)

例如:

    CONDITION_VARIABLE cv;
    InitializeConditionVariable(&cv);
    auto res = WaitForSingleObject(cv.Ptr, INFINITE); //returns immediately
    if (res != WAIT_OBJECT_0) { 
        auto ec = GetLastError();
        std::cout << ec << "\n";
    }

那么,你真的可以等待一个条件变量还是只是一个都市传说?

【问题讨论】:

  • 条件变量是用户模式对象。 WaitFor... 只能等待内核句柄。
  • 我在链接的答案中看不到任何暗示,你是怎么做的。首先,它是在谈论boost::condition_variable,而不是std::condition_variableCONDITION_VARIABLE。并且没有任何答案暗示WaitForXxx 系列函数可以与这些函数一起使用(接受的答案明确指出“如果您愿意将代码限制为 Windows 同步原语”)。抱歉,-1 表示研究不足。
  • 如果您愿意将您的代码限制为 Windows 同步原语",那 "又如何呢? std::condition_variable 不是 Windows 同步原语。
  • 我认为你做不到。你用SleepConditionVariableCS 等待一个。我不确定等待多个条件变量是否有意义,因为这意味着您持有几个关键部分并希望一次释放所有关键部分。 (这可能是有道理的,但我相信这种情况非常罕见,以至于您需要自己实现。)
  • @DavidHaim 我相信您最好的选择是您链接的问题的公认答案中的方法。 (也就是说,使用一个条件变量并在发出信号时检查世界的状态。)

标签: c++ windows winapi synchronization


【解决方案1】:

我不这么认为,也没有任何意义。

首先,WaitForXxx 函数(主要)在调度程序对象上运行 - 内核对象的子集,包括计时器、事件、互斥体、信号量、线程和进程(以及一些内部对象类型,如 KAGTEs 和KQUEUEs,但不能访问具有DISPATCHER_HEADER 的令牌或文件映射对象。它当然不适用于内核不知道的用户模式构造。

其次,请注意,当您在条件变量上休眠(“等待”)时,您必须使用正确的函数(SleepConditionVariableCSSleepConditionVariableSRW。同样,Windows(不仅是内核)不知道您要传递什么样的条件变量,但它需要这些信息才能正确运行。由于您没有向WaitForXxx 提供此信息,因此它们不能与条件变量一起使用。

【讨论】:

    【解决方案2】:

    您的问题的简单答案是。您不能将WaitForXxx 函数与Windows 同步API 提供的condition variables 一起使用。来自链接的文档:

    条件变量是同步原语,使线程能够等待特定条件发生。条件变量是不能跨进程共享的用户模式对象。

    WaitForXxx 函数接受泛型 HANDLE 类型的参数,它表示内核对象的句柄。条件变量是 user-mode 对象,而不是 kernel 对象,因此您不能将它们与这些函数一起使用,因为它们仅适用于内核对象。

    此外,这些函数的文档非常明确地说明了它们可以等待哪些类型的对象,并且条件变量不在该列表中。例如,WaitForMultipleObjects 说:

    WaitForMultipleObjects 函数可以在 lpHandles 数组中指定以下任何对象类型的句柄:

    • 更改通知
    • 控制台输入
    • 事件
    • 内存资源通知
    • 互斥体
    • 处理
    • 信号量
    • 线程
    • 等待计时器

    它们都有相同的列表,所以没有混淆。

    从技术上讲(我们在这里深入研究未记录的实现细节,所以你不应该把它当作福音),Win32 WaitForSingleObjectWaitForMultipleObjects 函数是基于 KeWaitForSingleObjectKeWaitForMultipleObjects内核子系统提供的功能。您可以将内核支持的对象分为三个基本类别:调度程序对象、I/O 对象/数据结构以及其他所有对象。第一类,调度程序对象,是最低级别的对象,它们都在它们的主体中使用相同的DISPATCHER_HEADER 数据结构表示。 Dispatcher 对象是唯一“可等待”的对象类型。根据定义,正是这个DISPATCHER_HEADER 结构使对象可等待。如果使用此数据结构表示对象,则可以将其传递给内核同步函数。因此,相同的规则将适用于 Win32 函数。

    整个问题似乎是基于 Managu 在his answer 中所做的一个声明:“Windows 有 WaitForMultipleObjects as aJ 张贴,如果您愿意将代码限制为 Windows 同步,这可能是一个解决方案原语。” 也许他不认为条件变量(因为它们是由 Windows 实现的)是同步原语,或者也许他错了。他提到的 aJ 的回答非常清楚地表明 WaitForMultipleObjects 用于“等待多个内核对象”,并且我们已经确定条件变量不是内核对象。无论哪种方式,我都没有看到任何“都市传说”的证据表明你可以做到这一点。

    显然,您不能将WaitForXxx 系列函数与boost::condition_variablestd::condition_variable 或其他任何函数一起使用。我相信你已经知道了,但是你的问题让一些人感到困惑,因为它链接到一个涉及 Boost 实现的问题。

    我不清楚为什么需要同时等待多个条件变量。我想你可以编写自己的条件变量实现,基于经典的 Win32 同步原语,例如互斥锁,然后你可以等待 WaitForMultipleObjects。您可能可以在网上找到此类代码的示例,因为条件变量直到 Vista 才成为操作系统的一部分。例如,this article 讨论了在 Windows 中实现条件变量的策略,因为它们是由 POSIX Pthreads 规范定义的。你也可以考虑使用Event Objects

    【讨论】:

    • 该列表不包括文件句柄,尽管您可以等待它们,因此存在一些混淆。既然我们已经确定这个列表并不详尽,那么列表中没有 whatever 就不能作为证据。
    • @conio 好吧,列表不包含文件句柄是有原因的。它们仅在涉及重叠 I/O 的非常特定、有限的情况下才可等待。对于重叠 I/O,一个事件已经与操作相关联,因此您实际上不是在等待文件句柄,而是在等待该事件。另见:WaitForSingleObject on a file handle?
    • 我没有说没有原因。我说这份清单并不详尽。这是事实,仅此而已。我并没有说等待文件句柄非常有用或类似的东西。因此,除了不正确之外,您的其余评论无关紧要。您不必必须设置OVERLAPPED 结构的hEventfield,如果您一次只有一个异步操作或者您不关心它的完成,则没有必要(请参阅MSDN和里希特书中的例子)。再说一遍:这并不是非常有用,但没有必然与操作相关的事件。
    • 如果您调用WaitForSingleObject 将文件传递给您正在等待文件句柄的句柄。否则争论是没有意义的。确实,您不会等待位于FILE_OBJECT 开头的DISPATCHER_HEADER,而是在结构末尾附近,但那又如何?我没有说别的。请参阅 IInspectable 对您链接到的问题的已接受答案的评论以及第二个答案中的第一个引号。
    猜你喜欢
    • 2015-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多