Skip to content

Latest commit

 

History

History
183 lines (120 loc) · 5.35 KB

File metadata and controls

183 lines (120 loc) · 5.35 KB

Condition variable

The two important methods of a condition variable are:

✔ wait() - invoked to make a thread sleep and give up resources

✔ notify() - invoked by a thread when a condition becomes true and the invoking threads want to inform the waiting thread or threads to proceed

👉 A condition variable is always associated with a lock. The lock can be either reentrant or a plain vanilla lock. The associated lock must be acquired before a thread can invoke wait()or notify() on the condition variable.

Idiomatic use of wait()

acquire lock
while(condition_to_test is not satisfied):
    wait

# condition is now true, perform necessary tasks

release lock

Spurious Wakeups

A peculiarity of condition variables is the possibility of spurious wakeups. It means that a thread might wakeup as if it has been signaled even though nobody called notify() on the condition variable in question. This is specifically allowed by the POSIX standard because it allows more efficient implementations of condition variables under some circumstances. Such wakeups are called spurious wakeups.

A thread that has been woken up does not imply that the conditions for it to move forward hold. The thread must test the conditions again for validity before moving forward. In conclusion, we must always check for conditions in a loop and wait() inside it 👆 python code above.

Idiomatic use of notify

acquire lock
set condition_to_test to true/satisfied
notify
release lock

Quizes

Consider an abridged version of the code we discussed in this lesson. The child_task method exits without releasing the lock. What would be the outcome of running the program? The changed program is shown below:

flag = False

lock = Lock()
cond_var = Condition(lock)


def child_task():
    global flag
    name = current_thread().getName()

    cond_var.acquire()
    while not flag:
        cond_var.wait()
        print("\n{0} woken up \n".format(name))

    print("\n{0} exiting\n".format(name))


if __name__ == "__main__":
    thread1 = Thread(target=child_task, name="thread1")
    thread1.start()
    
    # give the child task to wait on the condition variable
    time.sleep(1)

    cond_var.acquire()
    flag = True
    cond_var.notify_all()
    cond_var.release()

    thread1.join()
    print("main thread exits")
Answer is :

This is an interesting case, the single waiting thread exits without releasing the lock but since no other thread including the main thread attempts to acquire the lock the program sucessfully completes with the lock in locked state

Question2

from threading import Thread
from threading import Condition
import time

flag = False
lock = Lock()
cond_var = Condition()


def child_thread():
    cond_var.acquire()
    while not flag:
        cond_var.wait()

    # enter a useless loop, till flag becomes false
    while flag:
        None


childThread = Thread(target=child_thread)
childThread.start()

# Let the child thread wait on the condition variable
time.sleep(1)

cond_var.acquire()
flag = True
cond_var.notify()
cond_var.release()

time.sleep(1)

cond_var.acquire()
flag = False
cond_var.notify()
cond_var.release()

childThread.join()
print("Program successfully exits")
Answer is:

The child thread never releases the condition variable after the first loop, causing the main thread to block on the second acquire of the cond_var

Question3:

from threading import Condition
from threading import Thread
from threading import current_thread

flag = False

cond_var = Condition()


def child_task():
    global flag
    name = current_thread().getName()

    cond_var.acquire()
    if not flag:
        cond_var.wait()
        print("\n{0} woken up \n".format(name))

    flag = False
    cond_var.release()

    print("\n{0} exiting\n".format(name))


thread1 = Thread(target=child_task, name="thread1")
thread2 = Thread(target=child_task, name="thread2")

thread1.start()
thread2.start()

cond_var.acquire()
cond_var.notify_all()

thread1.join()
thread2.join()

print("main thread exits")
Answer is:

The main thread forgets to release() the lock after it notifies all the waiting threads. The woken up threads are unable to acquire the lock and return from the wait() call.

python threads - how do "condition.wait" and "condition.notifyAll" work?

The wait() method releases the lock, and then blocks until it is awakened by a notify() or notifyAll() call for the same condition variable in another thread. Once >awakened, it re-acquires the lock and returns. It is also possible to specify a timeout.

and:

Note: the notify() and notifyAll() methods don’t release the lock; this means that the thread or threads awakened will not return from their wait() call immediately, >but only when the thread that called notify() or notifyAll() finally relinquishes ownership of the lock.ownership of the lock.

Only one thread can have the lock at any time, of course: that's the core purpose of having a lock in the first place, after all!

So, notifyAll puts all waiting threads in ready-to-run state, and intrinsically all waiting to acquire the lock again so they can proceed: once the notifier releases the lock, one of the threads waiting to acquire that lock does acquire it (the others, if any, keep waiting for the lock to be released again, of course, so that only one thread has the lock at any given time).