Can another thread release a lock held by another thread in Python?

Question:

I am trying to understand threading in Python via this website. Here, it has the following code for a single producer/consumer-type problem:

import random 

SENTINEL = object()

def producer(pipeline):
    """Pretend we're getting a message from the network."""
    for index in range(10):
        message = random.randint(1, 101)
        logging.info("Producer got message: %s", message)
        pipeline.set_message(message, "Producer")

    # Send a sentinel message to tell consumer we're done
    pipeline.set_message(SENTINEL, "Producer")

def consumer(pipeline):
    """Pretend we're saving a number in the database."""
    message = 0
    while message is not SENTINEL:
        message = pipeline.get_message("Consumer")
        if message is not SENTINEL:
            logging.info("Consumer storing message: %s", message)

if __name__ == "__main__":
    format = "%(asctime)s: %(message)s"
    logging.basicConfig(format=format, level=logging.INFO,
                        datefmt="%H:%M:%S")
    # logging.getLogger().setLevel(logging.DEBUG)

    pipeline = Pipeline()
    with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
        executor.submit(producer, pipeline)
        executor.submit(consumer, pipeline)

class Pipeline:
    """
    Class to allow a single element pipeline between producer and consumer.
    """
    def __init__(self):
        self.message = 0
        self.producer_lock = threading.Lock()
        self.consumer_lock = threading.Lock()
        self.consumer_lock.acquire()

    def get_message(self, name):
        logging.debug("%s:about to acquire getlock", name)
        self.consumer_lock.acquire()
        logging.debug("%s:have getlock", name)
        message = self.message
        logging.debug("%s:about to release setlock", name)
        self.producer_lock.release()
        logging.debug("%s:setlock released", name)
        return message

    def set_message(self, message, name):
        logging.debug("%s:about to acquire setlock", name)
        self.producer_lock.acquire()
        logging.debug("%s:have setlock", name)
        self.message = message
        logging.debug("%s:about to release getlock", name)
        self.consumer_lock.release()
        logging.debug("%s:getlock released", name)

IIUC, the consumer lock is acquired and released in different threads (and the producer too). Is this possible? It feels like I’m missing something because then couldn’t any thread release a lock held by another thread?

Asked By: Victor M

||

Answers:

The answer is in the documentation:

The class implementing primitive lock objects.
Once a thread has acquired a lock, subsequent attempts to acquire it
block, until it is released; any thread may release it.

In fact this technique is frequently used to orchestrate the threads. If you look closely two Lock objects are created in the Pipeline class. The consumer_lock is acquired in initialization which means if two threads start concurrently, the consumer thread would hit its Lock object in self.consumer_lock.acquire() and gonna be blocked. It’s gonna wait for producer thread to put something in there. There has to be something to be consumed after all.

When producer puts something in, it releases the consumer_lock so that it can go ahead consumes it.

Answered By: S.B
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.