title: making a python dictionary threadsafe like queue
body: <p>While I like queue a lot, while coding I faced an issue which cannot be done if I am using queue. Basically I have a producer-worker architecture where many producers are producing jobs and many workers are consuming and processing them. Queue was a natural choice for that.</p>
  
  <p>However, because of the sheer amount of jobs being generated, I came up with the solution to batch my jobs while they are still waiting to be processed, and to possibly weed out some of them from the queue so that the queue will not have many items. E.g. for some jobs, the timestamp has expired and they no longer needs to be processed. Instead of workers to figure this out (which is costly in my case, and I cannot touch workers anyways), I want to weed out these kinds of results from the queue while these jobs are still waiting to be processed.</p>
  
  <p>So since I cannot just take items from in-between queue, I decided to change the architecture from queue to dictionary(cannot use list because I need key/value pair for some reason) and have the dict implemented in such a way that it behaves exactly like the queue. So here was my to-do list:</p>
  
  <ol>
  <li>The new dict must have push, pop methods, just like queue.</li>
  <li>The new dict must be threadsafe. Because many workers are consuming them, and we don't want to process the same result twice. With thread-safe, I can be sure that only one worker will take that job. Also, being thread safe would mean that the dictionary is being mutated by only one consumer at a time and hence all changes will be atomic in nature.</li>
  <li>The pop() method of the new dict must wait for new jobs to arrive, in case the dict is empty, just like the get() method in the python queue where it waits for the items to arrive in case the queue is empty.</li>
  </ol>
  
  <p>So I went and implemented the below code. Please note the use of lock.acquire()</p>
  
  <pre><code>def put(self, userId,  job):
      self.__lock.acquire()
  
      try:
          if userId in self.__queue:
              #merge the 'job' and the dict already present in 'self.__queue[userId]'
          else:
              self.__queue[userId] = job
      finally:
          self.__lock.release()
  
  def pop(self, wait = True):
      self.__lock.acquire()
  
      try:
          if wait:
              while(len(self.__queue) == 0):
                  pass
  
          #pop the 'first' value in dict.
          #Please do not worry that dict has no first or last value.
          #I sort the dict keys, and pop the first value.
      finally:
          self.__lock.release()
  </code></pre>
  
  <p><strong>Now notice the pop() method. Suppose there is no data in the dict, so one of the worker will be holding the lock, and other workers will be waiting for the lock to release. However the same lock is being used by the push() method also, and hence its a deadlock case. No data will even enter the dict because pop() is holding the lock.
  But if I remove the lock from the push method(), so that data can come anytime, I am worried about overwriting some data in worker's race condition. See below for an example.</strong></p>
  
  <p>In the else condition, where a new record is being created if that user is not present in the dict - I do not want that one worker creates the new key, and at the same time, another worker also creates the same key, and this new key overwrites the old key, thus me loosing data. I understand its a very small window of error, and if you think that its practically equal to zero, I am ready to remove the lock from the push() method altogether.</p>
  
  <p>There's a fat chance that I am being overly foolish and missing some point, but I cannot think of anything right now. I appreciate you taking your time to solve this. :)</p>
  
  <h1>Edit:</h1>
  
  <p>So according to what @Kevin said, here is my version of "conditions".</p>
  
  <pre><code>def put(self, userId,  job):
      self.__condition.acquire()
      try:
          if userId in self.__queue:
              #merge the 'job' and the dict already present in 'self.__queue[userId]'
          else:
              self.__queue[userId] = job
      finally:
          self.__condition.notify()
          self.__condition.release()
  
  def pop(self, wait = True):
      self.__condition.acquire()
  
      try:
          if wait:
              while True:
                  if(len(self.__queue) &gt; 0):
                      break
                  self.__condition.wait()
  
          #pop the 'first' value in dict.
          #Please do not worry that dict has no first or last value.
          #I sort the dict keys, and pop the first value.
      finally:
          self.__condition.release()
  </code></pre>
  
  <p>This is my first time with conditions, and hence I am putting my code here just so that others can verify. Thank You.</p>