I've written a Qt console application to try out QSemaphores and noticed some strange behavior. Consider a semaphore with 1 resource and two threads getting and releasing a single resource. Pseudocode:

Seems straightforward, but the threads will often fail to get a resource. A way to fix this is to put the thread to sleep for a bit after sem.release(1). What this tells me is that the release() member does not allow other threads waiting in tryAquire() access to the semaphore before the current thread loops around to the top of while(1) and grabs the resource again.

This surprises me because similar testing with QMutex showed proper behavior... i.e. another thread hanging out in QMutex::tryLock(timeout) gets notified properly when QMutex::unlock() is called.

1 Answer
1

I'm not able to fully test this out or find all of the suppporting links at the moment, but here are a few observations...

First, the documentation for QSemaphore.tryAcquire indicates that the timeout value is in milliseconds, not seconds. So your threads are only waiting 1 millisecond for the resource to become free.

Secondly, I recall reading somewhere (I unfortunately can't remember where) a discussion about what happens when multiple threads are trying to acquire the same resource simultaneously. Although the behavior may vary by OS and situation, it seemed that the typical result is that it is a free-for-all with no one thread being given any more priority than another. As such, a thread waiting to acquire a resource would have just as much chance of getting it as would a thread that had just released it and is attempting to immediately reacquire it. I'm unsure if the priority setting of a thread would affect this.

So, why might you get different results for a QSemaphore versus a QMutex? Well, I think a semaphore may be a more complicated system resource that would take more time to acquire and release than a mutex. I did some simple timing recently for mutexes and found that on average it was taking around 15-25 microseconds to lock or unlock one. In the 1 millisecond your threads are waiting, this would be at least 20 cycles of locking and unlocking, and the odds of the same thread always reacquiring the lock in that time are small. The waiting thread is likely to get at least one bite at the apple in the time that it is waiting, so you won't likely see any acquisition failures when using mutexes in your example.

If, however, releasing and acquiring a semaphore takes much longer (I haven't timed them but I'm guessing they might), then it's more likely that you could just by chance get a situation where one thread is able to keep reacquiring the resource repeatedly until the wait condition for the waiting thread runs out.

Thanks for your reply. This is interesting behavior and I'm glad I now know it. I assumed that releasing a resource like a semaphore or mutex would immediately look for waiting threads and perform a context switch to grant them the resource in place. Instead, it looks like OS scheduling continues as normal. I'm sure a lot of this is OS-dependent, but it's good to know that some behave like this. P.S. I am using tryAquire(1, 1000)... just put the 1 sec to clarify the pseudocode.
–
BabaBooeyNov 4 '11 at 14:37

Your explanation still makes perfect sense, even with the 1 second wait time. The wait time can be any amount of time and this behavior will apply. It's all about the fact that whichever thread grabs the resource first can release it, then immediately grab it again.
–
BabaBooeyNov 4 '11 at 14:40

1

I posted this question to the Qt boards and got a similar response: link
–
BabaBooeyNov 4 '11 at 14:47