简体   繁体   中英

Reset counter of semaphore in WinAPI

I have a queue with one consumer and multiple producers. It is based on Semaphore created with CreateSemaphore() .

While queue is empty Semaphore is set to zero. Producer puts message to a queue and increments counter so consumer waits for item in a queue.

There is a case that requires to clear queue. That means that semaphore counter must be reset to 0 .

Unfortunately, I did not found an option on MSDN to reset counter. Usage of WaitForSingleObject() while counter is not zeroed creates racing conditions, so doesn't seem to be an option.

Is there any another way to reset semaphore counter in Windows?

Literal answer: no, you cannot atomically reset a semaphore.


In the single consumer case, you probably shouldn't be using a semaphore in the first place. An auto-reset event is sufficient, with a consumer loop like this:

  • try to pop an item from the queue
  • if successful, process it; return to top of loop
  • if the queue is empty, wait on the event, then return to top of loop

With this logic, you can clear the queue without needing to do anything to the event.

Note that if the producer/consumer logic can be integrated with the queue's own locking mechanism, it may be more efficient to use a condition variable.


A more generic option for the single consumer case (assuming a FIFO queue) is to set a flag for the consumer, then add a guard message at the end of the queue.

Whenever the consumer takes a message off the queue, it can check the flag, and if set, discard all messages until the guard message arrives.

(If another queue clear might be attempted while the consumer is still processing a previous one, then you need some additional locking. This can just be an auto-reset event that is set initially, waited on before setting the flag, and then set again by the consumer when it sees the guard message.)


In the multiple consumer case, one simple approach is to use a SRW lock (as Hans suggested) in combination with a semaphore:

  • To add an item to the queue, obtain a reader ("shared") lock, add the item, increment the semaphore, release the lock.

  • To remove an item from the queue, wait for the semaphore, obtain a reader ("shared") lock, remove the item, release the lock.

  • To empty the queue, obtain a writer ("exclusive") lock, clear out the queue, repeatedly wait for the semaphore until it is empty, release the lock.

In rare instances, at the point where you obtained the writer lock, one of the consumer threads will have just decremented the semaphore and be about to try obtaining a reader lock. When that thread finally gets the lock, it will find that the queue is empty. This is harmless, though if you wanted to, you could detect threads in this state (by noticing that the number of items removed from the queue was greater than the number of times you decremented the semaphore) and leave one or more dummy items in the queue for them to find and discard.

How about calling WaitForSingleObject(semaphore, 0) to attempt to obtain the semaphore with 0 waiting time? This effectively resets a single count semaphore. You may need to repeat the call if has multiple counts.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM