1

Imagine having the two threads observeT and upT. observeT observes the value of an instance attribute (instance.a) and should 'alert' (print a note in this example) if its value is 7. Then there's the thread upT, which increases the value of the instance attribute by 1 at a time (instance.a += 1).

However, due to the randomly chosen thread to continue with Python's Lock we can't make sure that the observer thread (observeT) catches the moment when the value of instance.a was increased to 7.

How do I make sure that the observer is called every time after upT releases to lock? Note that it is important to keep the threads upT and observeT split.

Please see the following code for more details:

from threading import Lock, Thread


class MyClass():
    a: int

    def __new__(cls):
        instance = super().__new__(cls)
        instance.a = 0
        return instance

instance = MyClass()
lock = Lock()

def up():
    for i in range(100000):
        with lock:
            instance.a += 1

def observe():
    while True:
        with lock:
            a = instance.a
            if a == 7:
                print("This is 7!")
            if instance.a == 100000:
                break

observeT = Thread(target=observe)
upT = Thread(target=up)

observeT.start()
upT.start()

upT.join()
observeT.join()

Thank you for your help!

1
  • 1
    Side Note: Your main thread calls upT.start() and then it immediately calls upT.join(). That means, your main thread doesn't do anything concurrently with the upT thread. But, concurrency is the only reason for having threads. So why create the upT thread at all? The main thread already exists. Why not use it? Why not just do: observeT.start(); up(); observeT.join() ? Commented Jun 25, 2020 at 9:40

1 Answer 1

1

Is this what you're looking for?

from threading import Thread, Lock, Condition


class MyClass:
    def __init__(self, a_lock):
        self.cond = Condition(a_lock)
        self.canproceed = False
        self.a = 0

    def __setattr__(self, key, value):
        super().__setattr__(key, value)
        if key == 'a':
            if value == 7 or value == 100000:
                self.cond.notify()
                if value == 7:
                    while not self.canproceed:
                        self.cond.wait()


lock = Lock()
instance = MyClass(lock)


def up():
    for i in range(100000):
        with lock:
            instance.a += 1


def observe():
    with instance.cond:
        while instance.a != 7:
            instance.cond.wait()
        print("This is 7!")
        instance.canproceed = True
        instance.cond.notify()
        while instance.a != 100000:
            instance.cond.wait()




observeT = Thread(target=observe)
upT = Thread(target=up)

observeT.start()
upT.start()

upT.join()
observeT.join()

Output:

This is 7!
Sign up to request clarification or add additional context in comments.

8 Comments

Thank you so much - this is it!
Hi, I have one more question: Let's say we can't change up() and everything up() does is increasing instance.a, like this: with cond: instance.a += 1 ... how can I make observe() still notices that change?
@5t4cktr4c3 If that's the case, you can't guarantee that observe will notice when a becomes exactly 7.
Unfortunately, that's the exact scenario I have ... any workarounds or other solutions?
@5t4cktr4c3 Updated to reflect your scenario where the up method is untouched.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.