Queue qsize = unreliable?

Ames Andreas (MPA/DF) Andreas.Ames at tenovis.com
Mon Aug 9 13:02:18 EDT 2004


Dave Brueck wrote:

> But is it blocking in any way that you (the caller) will likely
> notice? (probably not - the mutex is held for a relatively short

How I notice a blocking function?

1) As a programmer:  I have to read the docs and unfortunately all too
   often the code.

2) As a "caller":  I'm wasting time when blocking without a reason.

> period of time. Note also that the mutex that gets acquired is not
> the "main" mutex of the Queue.)

In CVS it acquires the only lock that is there, in the 2.3 version and
below it acquires the mutex, that I'd call the "main" one, just after
checking (by a non-blocking acquiration of esema) if the Queue is
empty.

>> With CPython's assertions about atomicity of certain list
>> operations it's possible to implement a lock free queue that is
>> even usable for the single producer or single consumer case.
>
> If you have the single producer and/or single consumer case, you
> don't need a Queue at all - just use a built-in list (or in 2.4 a
> collections.deque object).

I think I didn't make my point clear enough.  The most important word
in the above paragraph was "usable", which means more to me than just
thread-safe.  I'd be interested in a usable (i. e. non-polling)
implementation of a queue without any locks.

>> if !q.empty():
>>    reliableCode()
>> else
>>    unreliableCode()
>> should be absolutely okay.
>
> It'd probably _work_, yes, but would it offer any real advantage? On
> that particular mutex, there's almost never any contention at all -
> *especially* in e.g. the single consumer case. And, even if there is
> some contention, it is acquired for an extremely short amount of
> time (relatively speaking) - which is why the caller is not likely
> to notice that any locking is going on under the covers. The cost of
> having both reliableCode and unreliableCode paths seems to outweigh
> any potential advantage IMO.

I just included the "else" branch to make clear that only one of
empty's two possible outcomes is reliable.  It makes much more sense
and it is much closer to my specific application to write:

while !q.empty():
      doSomethingToTheWorkersOutput()

When leaving this loop I'll go back to sleep on select.

As to the "extremely short amount of time (relatively speaking)":  I'm
still very impressed by Dan Kegel's c10k paper.  And following that
one I'm not so much concerned about the amount of time that a producer
may hold the lock (that time may very well be ignorable), when the
single consumer tries to acquire it, but much more about the fact that
it potentially needs at least *two* context switches per iteration
until the one thread that feeds all others and thus that all others
depend upon gets back to work.


cheers,

andreas




More information about the Python-list mailing list