Home > database >  Python Lock always re-acquired by the same thread
Python Lock always re-acquired by the same thread

Time:08-13

I got this as an interview problem a few days ago. I don't really know parallel programming, and the obvious solution I've tried isn't working.

The question is: write two functions, one printing "foo", one printing "bar", that will be run on separate threads. How to ensure output is always:

foo
bar
foo
bar
...

Here's what I've tried:

from threading import Lock, Thread


class ThreadPrinting:
    def __init__(self):
        self.lock = Lock()
        self.count = 10

    def foo(self):
        for _ in range(self.count):
            with self.lock:
                print("foo")

    def bar(self):
        for _ in range(self.count):
            with self.lock:
                print("bar")


if __name__ == "__main__":
    tp = ThreadPrinting()
    t1 = Thread(target=tp.foo)
    t2 = Thread(target=tp.bar)
    t1.start()
    t2.start()

But this just produces 10 "foo"s and then 10 "bar"s. Seemingly the same thread manages to loop around and re-acquire the lock before the other. What might be the solution here? Thank you.

CodePudding user response:

this just produces 10 "foo"s and then 10 "bar"s. Seemingly the same thread manages to loop around and re-acquire the lock before the other.

No surprise there. The problem with using a threading.Lock object (a.k.a., a "mutex") in this way is that, like the (default) mutexes in most programming systems, it makes no attempt to be fair.

The very next thing that either of your two threads does after it releases the lock is, it immediately tries to acquire the lock again. Meanwhile, the other thread is sleeping (a.k.a., "blocked",) waiting for its turn to acquire the lock.

The goal of most operating systems, when there is heavy demand for CPU time, is to maximize the amount of useful work that the CPU(s) can do. The best way to do that is to award the lock to the thread that already is running on some CPU instead of wasting time waking up some other thread that is sleeping.

That strategy works well in programs that use locks the way locks were meant to be used—that is to say, programs where the threads spend most of their time unlocked, and only briefly grab a lock, every so often, in order to examine or update some (group of) shared variables.


In order to make your threads take turns printing their messages, you are going to need to find some way to let the threads explicitly say to each other, "It's your turn now."

See my comments on your question for a hint about how you might do that.

CodePudding user response:

The way I did it was just to have the first thread send 'foo' then 1 second of sleep before the second sends 'bar'. Both functions sleep for 2 seconds between sends. This allows for them to always alternate, sending one word per second.

from threading import Thread
import time

def foo():
    num = 0
    while num < 10:
        print("foo")
        num = num   1
        time.sleep(2)

def bar():
    num = 0
    while num < 10:
        print("bar")
        num = num   1
        time.sleep(2)

t1 = Thread(target=foo)
t2 = Thread(target=bar)
t1.start()
time.sleep(1)
t2.start()

I tried this for 100 of each 'foo' and 'bar' and it still alternated.

  • Related