Kostas Oreopoulos
Kostas Oreopoulos

Reputation: 93

Passing a Lock as an argument in multiprocessing. Why it works?

In the following code snippet, I see by printing the address of Lock objects, that each process gets a copy of the LOCK object.

""" Processes and Locks """

import multiprocessing as mp
import logging


logging.basicConfig(level=logging.DEBUG,
                    format='(%(processName)-10s) (%(threadName)-9s) %(message)s',)


def increase_value(iterations, value, lock):
    """Increase a shared variable"""
    print(hex(id(lock)))
    for _ in range(iterations):
        lock.acquire()
        value.value = value.value + 1
        lock.release()


def decrease_value(iterations, value, lock):
    """Decrease a shared variable"""
    print(hex(id(lock)))
    for _ in range(iterations):
        lock.acquire()
        value.value -= 1
        lock.release()


if __name__ == "__main__":
    ITERATIONS = 100000
    SHARED_VALUE = mp.Value('i', 0)
    LOCK = mp.Lock()
    print(hex(id(LOCK)))
    t1 = mp.Process(target=increase_value, 
                    args=(ITERATIONS, SHARED_VALUE, LOCK))
    t2 = mp.Process(target=decrease_value, 
                    args=(ITERATIONS, SHARED_VALUE, LOCK))

    t1.start()
    t2.start()

    t1.join()
    t2.join()

    logging.debug("SHARED VALUE %d", SHARED_VALUE.value)

Why does that work? lock.acquire should make sense if they refer to the same lock. What am I missing?

Upvotes: 0

Views: 750

Answers (1)

Blender
Blender

Reputation: 298166

lock.acquire should make sense if they refer to the same lock

They do refer to the same lock, just not to the same Python object (whatever that means across Python processes).

Pretend you have this very poorly designed lock:

import os
import time

class VeryDumbLock:
    def __init__(self, filename):
        self.filename = filename

    def acquire(self):
        while os.path.exists(self.filename):
            time.sleep(0.1)

        with open(self.filename, 'w'):
            pass

    def release(self):
        os.remove(self, self.filename)

If you create foo = VeryDumbLock('/tmp/lock') and bar = VeryDumbLock('/tmp/lock') in two different processes, both of them really are the same lock, even though they are clearly different objects residing at different addresses in memory.

The actual mechanism that the lock object implements in the background is the only thing you need to care about.

Upvotes: 1

Related Questions