Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 415
Victor M Avatar asked Aug 12 '26 19:08

Victor M


1 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.

like image 130
SorousH Bakhtiary Avatar answered Aug 15 '26 08:08

SorousH Bakhtiary