在 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);
我有一个包含一个消费者和多个生产者的队列。它基于使用 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);