Multiplex on queue.Queue?

后端 未结 4 2011
感情败类
感情败类 2021-02-13 22:19

How can I go about \"selecting\" on multiple queue.Queue\'s simultaneously?

Golang has the desired feature with its channels:

select {
case i1 = <-c1:         


        
4条回答
  •  迷失自我
    2021-02-13 22:45

    There are many different implementations of producer-consumer queues, like queue.Queue available. They normally differ in a lot of properties like listed on this excellent article by Dmitry Vyukov. As you can see, there are more than 10k different combinations possible. The algorithms used for such queues also differ widely depending on the requirements. It's not possible to just extend an existing queue algorithm to guarantee additional properties, since that normally requires different internal data structures and different algorithms.

    Go's channels offer a relatively high number of guaranteed properties, so those channels might be suitable for a lot of programs. One of the hardest requirements there is the support for reading / blocking on multiple channels at once (select statement) and to choose a channel fairly if more than one branch in a select statement is able to proceed, so that no messages will be left behind. Python's queue.Queue doesn't offer this features, so it's simply not possible to archive the same behavior with it.

    So, if you want to continue using queue.Queue you need to find workarounds for that problem. The workarounds have however their own list of drawbacks and are harder to maintain. Looking for another producer-consumer queue which offers the features you need might be a better idea! Anyway, here are two possible workarounds:

    Polling

    while True:
      try:
        i1 = c1.get_nowait()
        print "received %s from c1" % i1
      except queue.Empty:
        pass
      try:
        i2 = c2.get_nowait()
        print "received %s from c2" % i2
      except queue.Empty:
        pass
      time.sleep(0.1)
    

    This might use a lot of CPU cycles while polling the channels and might be slow when there are a lot of messages. Using time.sleep() with an exponential back-off time (instead of the constant 0.1 secs shown here) might improve this version drastically.

    A single notify-queue

    queue_id = notify.get()
    if queue_id == 1:
      i1 = c1.get()
      print "received %s from c1" % i1
    elif queue_id == 2:
      i2 = c2.get()
      print "received %s from c2" % i2
    

    With this setup, you must send something to the notify queue after sending to c1 or c2. This might work for you, as long as only one such notify-queue is enough for you (i.e. you do not have multiple "selects", each blocking on a different subset of your channels).

    Alternatively you can also consider using Go. Go's goroutines and concurrency support is much more powerful than Python's limited threading capabilities anyway.

提交回复
热议问题