1

This question pertains to Python 3.6.

I have a piece of code which has a thread from the threading library running, as well as the program's main thread. Both threads access an object that is not threadsafe, so I have them locking on a Condition object before using the object. The thread I spawned only needs to access/update that object once every 5 minutes, so there's a 5 minute sleep timer in the loop.

Currently, the main thread never gets a hold of the lock. When the second thread releases the Condition and starts waiting on the sleep() call, the main thread never wakes up/acquires the lock. It's as if the main thread has died.

class Loader:
    def __init__(self, q):
        ...
        self.queryLock = threading.Condition()
        ...
        thread = Thread(target=self.threadfunc, daemon=True)
        thread.start()
        ...
        self.run()

    def threadfunc(self):
        ...
        while True:
            self.queryLock.acquire()
            [critical section #1]
            self.queryLock.notify()
            self.queryLock.release()
            sleep(300)

    def run(self):
        ...
        while True:
            ...
            self.queryLock.acquire()
            [critical section #2]
            self.queryLock.notify()
            self.queryLock.release()
            ...
martineau
  • 119,623
  • 25
  • 170
  • 301
  • A possible issue is that you've used a Condition where really a basic Lock would suffice. According to the docs if you don't provide any parameters when initialising Condition it creates an underlying RLock (reentrant lock) to use wher really a basic Lock is all you need. Try initialising the Condition as Condition(lock=Lock()) or just replace it with a Lock – Simon Notley Sep 01 '20 at 20:38
  • Can you confirm that no thread ever waits for the condition? Also, can you show us the real code for the main thread? With the `...`, I can't tell what the main thread is actually doing most of the time. Also, what happens during the `[critical section #2]` code? It matters what the thread is doing while it holds the lock. Are you saying *neither* thread gets the lock? Or are you saying that one thread keeps getting the lock over and over and the other doesn't get a chance? – David Schwartz Sep 01 '20 at 20:39

1 Answers1

0

I believe you don't really need to use a Condition. It appears that a simple Lock would get the job done in your case. You don't actually verify that some condition is met and you don't use the Condition's special method wait().


That being said, regarding the code you provided, it seems that your main thread is too "quick", and re-acquires the lock before the other thread gets a chance. Here is a slightly modified version of your code in which the main thread is waiting a bit, giving the other thread a chance, and it successfully acquires the lock and continue.

class Loader:
    def __init__(self):
        self.queryLock = threading.Condition()
        thread = Thread(target=self.threadfunc, daemon=True)
        thread.start()
        self.run()
    
    def threadfunc(self):
        while True:
            self.queryLock.acquire()
            print("critical section 1")
            time.sleep(1)
            self.queryLock.notify()
            self.queryLock.release()
            time.sleep(5)
    
    def run(self):
        while True:
            self.queryLock.acquire()
            print("critical section 2")
            time.sleep(2)
            self.queryLock.notify()
            self.queryLock.release()
            print("main is waiting a bit")
            time.sleep(1)


Loader()

Race conditions are fun :)

Jona
  • 669
  • 10
  • 18
  • Re, "...your main thread is too 'quick'", there's a name for that; "starvation." It's what we say when patterns of locking and/or signalling contrive to make one (or more) thread(s) always late for dinner. The main thread in this example is _starving_ the new thread. – Ohm's Lawman Sep 01 '20 at 21:13