title: multi-threaded script hangs at the end
body: <p>Hello:  I am trying to get this script to work.  Sometimes depending on the number of users (the example below shows 3 but that can be easily increased), the script does not exit.  All the jobs are done but the script just hangs without exiting.  I think my approach to existing from the <code>while True</code> in <code>worker</code> is the problem, but I do not know of an alternative.  Any ideas?</p>
  
  <pre><code>import datetime, logging, os.path, queue, random, threading, time
  
  script = os.path.basename(__file__)
  logging.basicConfig(level=logging.DEBUG, format="%(asctime)-4s %(thread)6s  %(message)s", datefmt="%m-%d %H:%M:%S",
      filename="%s_%s.log"%(script[:script.find(".")],datetime.datetime.today().strftime("%Y%m%d-%H%M%S")))
  
  class User(object):
  
      def __init__(self, id, ndelay, mind, maxd):
          self.id = id
          self.numdelay = ndelay #number of delays
          self.mind = mind       #min delay
          self.maxd = maxd       #max delay
          self.currdelaynum = 0  #index for next delay
  
      def hasDelay(self):
          if self.currdelaynum &gt;= 0 and self.currdelaynum &lt; self.numdelay:
              return True
  
      def runNextDelay(self):
          delay = round(self.mind + random.random()*(self.maxd - self.mind))
          logging.info("%s beg (delay=%d)"%(self.id,delay))
          time.sleep(delay)
          logging.info("%s end"%self.id)            
          self.currdelaynum += 1
  
  
  def worker(unext,udone):
      while True:
          if unext.qsize() &gt; 0:
              m = unext.get()
              users_all[m].runNextDelay()
              if users_all[m].hasDelay():
                  unext.put(m)
              else:
                  udone.put(m)
          else:
              if udone.qsize() &gt;= len(users_all):
                  break
  
  
  if __name__=='__main__':
      random.seed(10)
  
      #global users_all
      users_all = list()
      users_all.append(User("aa",2,3,9))
      users_all.append(User("bb",3,2,4))
      users_all.append(User("cc",1,4,5))
  
      users_next = queue.Queue()
      users_done = queue.Queue()
      for n in range(len(users_all)):
          users_next.put(n)
  
      threads = [threading.Thread(target=worker, args=(users_next,users_done)) for n in range(2)]
      for t in threads: t.start()
      for t in threads: t.join()
  </code></pre>
  
  <p>Most multithreaded python examples have a queue of jobs which are known upfront.  I am writing a script which tests the response times for queries running in parallel on a database.  To make the above example self-contained, I have replace the ODBC query part of it with <code>sleep</code>.  I would also appreciate any comments for a better implementation.</p>
  
  <p><strong>Updated version based on comments</strong></p>
  
  <pre><code>def worker(unext):
      while True:
          try:
              m = unext.get_nowait()
              users_all[m].runNextDelay()
              if users_all[m].hasDelay():
                  unext.put(m)
          except queue.Empty:
              break
  </code></pre>