Search code examples
pythonpython-2.7python-multithreading

I have trouble understanding locks in Python


I'm playing with threading.Lock and wrote the following test to further my understanding.

import threading
from time import sleep

LOCK = threading.Lock()

class Printer(threading.Thread):
    def __init__(self, *args, **kwargs):
        super(Printer, self).__init__(*args, **kwargs)
        self.i = 0

    def run(self):            
        while True:
            print(self.i)
            self.i += 1
            sleep(1)

p = Printer()
p.start()

raw_input() # block main thread until return pressed
LOCK.acquire()

# stop printer for 6 seconds
for _ in range(3):
    print('holding...')
    sleep(2)

# let printer continue
LOCK.release()

I expected the following output (comment not part of output):

0
1
2
# user pressed enter
holding...
holding...
holding...
3
4
5
...

However, the printer does not stop - the actual output I am getting is:

0
1
2
# user pressed enter
holding...
3
4
holding...
5
6
holding...
7
8
...

Obviously I am misunderstanding something here? How can I temporarily suspend the printer?


Solution

  • You must acquire the lock in your thread as well. A lock by itself does not do anything at all. Its only purpose is to make it so that you cannot acquire the lock twice - thus making two or more parts of the code mutually exclusive. Whoever gets the lock, can proceed while every other thread block while trying to acquire the lock.

    Consider this version of the code (I am using with but you can as well replace it with acquire/release.

    import threading
    from time import sleep
    
    LOCK = threading.Lock()
    
    class Printer(threading.Thread):
        def __init__(self, *args, **kwargs):
            super(Printer, self).__init__(*args, **kwargs)
            self.i = 0
    
        def run(self):
            while True:
                with LOCK:
                    print(self.i)
                    self.i += 1
                sleep(1)
    
    p = Printer()
    p.start()
    
    raw_input() # block main thread until return pressed
    LOCK.acquire()
    
    # stop printer for 6 seconds
    for _ in range(3):
        print('holding...')
        sleep(2)
    
    # let printer continue
    LOCK.release()
    

    This does now what you want it to do.

    This still is not "right" as your main thread exits as soon as it releases the lock. Normally this would be considered bad programming but in a demo script it probably does not matter. You just do not have any control over your thread anymore - it will run there until killed with a signal.