Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python Lock always re-acquired by the same thread

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.

like image 953
T. Spikes Avatar asked Oct 23 '25 00:10

T. Spikes


1 Answers

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.

like image 83
Solomon Slow Avatar answered Oct 24 '25 14:10

Solomon Slow