Condition Object

Condition

The condition object allows us to run a thread only if a requirement is met in another thread, and once it’s met, we can inform the waiting thread that the requirement has been met.

A real-life scenario of this would be; that a producer has run out of products in his store, now the consumers must wait before they can consume that product. Once the product is available, the producer notifies all of the waiting customers, that the product is in store.

Let us consider a simple example:

import threading

class Number_Class:

    def __init__(self):
        self.number_list = []

    def add_number(self, number_):
        print(f"Adding {number_} to the list\n")
        self.number_list.append(number_)

    def remove_number(self):
        # Removes item stored in index 0
        print(f"Removing {self.number_list[0]} from the list\n")
        self.number_list.remove(self.number_list[0])

def Adder_(number_class, adder_condition_):

    # Adds 0 & 1 to the number_list
    for i in range(2):

        # Locking the number_list
        adder_condition_.acquire()

        try:
            # Result of i * 100 is added to the number_list
            number_class.add_number(i * 100)

            # Inform waiting threads
            print("Adder Function: Notifying waiting threads")
            adder_condition_.notify()

        except:
            # If adding the number fails
            print("Adder Function: Could not add")

        finally:
            # Releasing the number_list
            adder_condition_.release()
            print("")  # making the output prettier

def Remover_(number_class, remover_condition_):

    # Locking the number_list
    remover_condition_.acquire()

    while True:
        try:
            # Delete the first number
            number_class.remove_number()

        except:
            print("Remover Function: Number list is empty, waiting to see if new number will be added\n")

            # Wait 5 seconds
            number_added = remover_condition_.wait(5)  # True if new number is added, else False

            if number_added:
                print("Remover function has been notified\n")

                # Skip this iteration and jump to the next iteration of the loop
                continue
            else:
                print("Remover Function: Waiting time has ended")
                print("Remover Function: No more numbers will be added\n")

                break # Escape the loop

    # Releasing the number_list
    remover_condition_.release()

# Condition Object
condition_object = threading.Condition()

# Number Class Object
number_class_object = Number_Class()

adder_variable = threading.Thread(target=Adder_, args=(number_class_object, condition_object))
remover_variable = threading.Thread(target=Remover_, args=(number_class_object, condition_object))

adder_variable.start()
remover_variable.start()

adder_variable.join()
remover_variable.join()

print("Script is done")

The output would be:

The Output

We use two threads; the first adds a number to a list and the second removes that number from the list. Using our first thread we add a number to the list, then we notify our second thread which is waiting for a number to be added. It removes the number we stored in the list, and because it is a repeating process, it keeps waiting for a new number to be added. We repeat this twice.

The second thread waits for a notification from the first thread for 3 seconds. If after 3 seconds it sees no notification regarding the addition of a new number, it proceeds to tell us that the waiting time is over and that numbers aren’t being added.

The Code

In Python, classes & functions are positioned first and the code that uses them comes afterward. Remembering that, we first created objects of the “Condition” class and our custom class called “Number_Class”. The names of these are “condition_object” and “number_class_object” respectively. 

The “Number_Class” has two functions that adds and removes a number from the list of numbers.

Then we created two threads for the custom functions “Adder_” and “Remover_”. (For simplicity, we will address these threads as ‘first thread’ and ‘second thread’ respectively). The parameters for these functions are the objects of our custom class and the “Condition” class. 

Both use a shared lock. The first thread sets a condition that if met will further the execution of the second thread. The first thread also notifies the second thread that the condition has been met and can continue execution.

The “Adder_” function adds two numbers to the number list. It acquires a common lock with the “Remover_” function and we use the try statement to handle any unforeseen error. We multiply the index number by 100 and add that result to our number list.

number_class.add_number(i * 100)

 

“number_class” is a variable that references the object of the class we created, and so to use a function from our class we only need to call this followed by the dot operator and the function name.

Once we have added our number, we need to notify the second thread that a number has been added and release the lock.

The “Remover_” function first acquires the shared lock. ‘while True’ gives us an infinite loop which we use to stop only when numbers aren’t added. The ‘break’ keyword stops an infinite loop.

Then it removes the added number and if there is nothing to be removed it waits 3 seconds to see if a new number is added. If so, a notification is displayed and that number is removed. The ‘continue’ keyword skips the current iteration of the loop and jumps to the next iteration of the loop.

NOTE: If you are unaware of an iteration, it is the nth repetition of a loop.

If there is no new number the program will simply inform us and break out of the infinite loop.

 

Tip:

  • If you do not see a similar order of the output add this to the beginning of the “Adder_” function.
time.sleep(1)
  • This happens as some PCs are quite fast and they’ll run the first thread before the second thread starts.

Condition Object

The syntax is:

  • <variable_name> = threading.Condition([lock])
    • [lock] – You can create a lock and if not provided it takes a default clock.

The two primary functions are acquire() and release(). Additional functions include:

  • wait(): This makes a thread release its lock and wait till it is awakened by notify() or notifyall().
  • notify(n): This wakes up ‘n’ number of waiting threads.
    • n – By default is 1.
  • notifyall(): This wakes up all of the waiting threads.

What have we learned?

  • What is the Condition object?
  • Which function creates a common lock shared among threads?
  • How do we inform a waiting thread that a condition has been met?
  • What is the use of the break and continue keyword?
  • What is the wait() function?
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments