在 WinAPI 中重置信号量计数器

Reset counter of semaphore in WinAPI

我有一个包含一个消费者和多个生产者的队列。它基于使用 CreateSemaphore().

创建的信号量

当队列为空时,信号量设置为零。生产者将消息放入队列并递增计数器,以便消费者等待队列中的项目。

有一种情况需要清空队列。这意味着信号量计数器必须重置为 0.

不幸的是,我没有在 MSDN 上找到重置计数器的选项。在计数器未归零时使用 WaitForSingleObject() 会产生竞争条件,因此似乎不是一个选项。

在Windows中是否还有其他重置信号量计数器的方法?

字面上的回答:不,您不能自动重置信号量。


在单个消费者的情况下,您可能一开始就不应该使用信号量。一个自动重置事件就足够了,使用这样的消费者循环:

  • 尝试从队列中弹出一个项目
  • 成功则处理; return 到循环顶部
  • 如果队列为空,等待事件,然后return到循环顶部

使用此逻辑,您无需对事件执行任何操作即可清除队列。

注意,如果producer/consumer逻辑可以和队列自身的锁机制结合起来,使用条件变量可能会更高效。


单个消费者案例(假设 FIFO 队列)的一个更通用的选项是为消费者设置一个标志,然后在队列末尾添加一个保护消息。

每当消费者从队列中取出一条消息时,它可以检查标志,如果设置了标志,则丢弃所有消息,直到守卫消息到达。

(如果消费者仍在处理前一个队列时可能会尝试另一个队列清除,那么您需要一些额外的锁定。这可以只是一个最初设置的自动重置事件,在设置标志之前等待,然后在消费者看到保护消息时再次设置。)


在多消费者情况下,一种简单的方法是结合使用 SRW 锁(如 Hans 所建议的那样)和信号量:

  • 要向队列添加一个项目,获得一个reader("shared")锁,添加项目,递增信号量,释放锁。

  • 要从队列中移除一个项目,等待信号量,获得一个reader("shared")锁,移除项目,释放锁。

  • 清空队列,获得writer("exclusive")锁,清空队列,反复等待信号量清空,释放锁。

在极少数情况下,在您获得写入者锁时,其中一个使用者线程将刚刚递减信号量并准备尝试获得 reader 锁。当那个线程最后拿到锁的时候,它会发现队列是空的。这是无害的,但如果你愿意,你可以检测处于这种状态的线程(通过注意到从队列中删除的项目数大于你递减信号量的次数)并在排队等待他们找到并丢弃。

调用 WaitForSingleObject(semaphore, 0) 尝试获取等待时间为 0 的信号量怎么样?这有效地重置了单个计数信号量。如果有多个计数,您可能需要重复调​​用。

while (WaitForSingleObject(handle, 0) == WAIT_OBJECT_0);