Semaphores in Python

Haider Ali Oct 10, 2023
Semaphores in Python

In this guide, we will learn how to handle multi-threading in Python using semaphores. How to have synchronized access to the threads and the restricted amount of resources?

Semaphores

A synchronization controller is a semaphore. Semaphore provides synchronized access to a limited number of resources for threads.

A semaphore can be viewed as a variable representing how many resources are available now.

For instance, a parking lot in a mall that functions as a semaphore has several spaces available on a given level.

The semaphore’s value must be greater than or less than the available resources. The operations of acquire and release are connected with the semaphore.

The value of the semaphore is decreased when one of the resources being used for synchronization is “acquired” by a thread. The value of the semaphore is increased when one of the synchronized resources is “released” by a thread.

Semaphores in Python

Python’s implementation of the semaphore concept uses a class from the threading module. Semaphore is the name of this class.

An acquire() and release() functions are both included in the Semaphore class, along with a function constructor.

If the semaphore count is more than zero, the acquire() function is used to lower the count. If not, it will block until the count is larger than 0.

One of the threads sitting on the semaphore is awakened by using the release() function, which also increases the semaphore’s count. Now let’s understand the syntax of the semaphore.

object_name = threading.Semaphore(count)

The Semaphore class object is indicated by the object_name in the syntax above.

The number of threads permitted to access at once is specified by the Semaphore class’s count argument. This parameter’s default value is 1.

The count parameter’s value is decreased by one each time a thread uses the acquire() function. The count parameter’s value is increased by one each time a thread uses the release() function.

According to this statement, anytime we call the acquire() method, the value of the count parameter will decrease; however, when we call the release() function, the value of the count parameter will be increased. Take a look at the following code.

# import threading module
import threading

# creating instance of semaphore
sema = threading.Semaphore(1)


def test():
    # appling semaphore
    print(f"Value Of Semaphore --> {sema._value}")
    sema.acquire()
    print(f"acquired lock --> {threading.current_thread().name}")
    print(f"Value Of Semaphore --> {sema._value}")
    print(f"release lock --> {threading.current_thread().name}")
    sema.release()
    print(f"Value Of Semaphore --> {sema._value}")


# initializing threads
t1 = threading.Thread(target=test)
t2 = threading.Thread(target=test)
t3 = threading.Thread(target=test)
# executing threads
t1.start()
t2.start()
t3.start()

The threads will be synchronized if count is set to 1, as in the above code. If we look at the output of the above code, we’ll notice that it’ll be the first and second thread, then the third thread will have access to the code between the acquire and release.

Value Of Semaphore --> 1
Value Of Semaphore --> 1
acquired lock --> Thread-1 (test)
Value Of Semaphore --> 0
Value Of Semaphore --> 0
release lock --> Thread-1 (test)
Value Of Semaphore --> 1
acquired lock --> Thread-2 (test)
Value Of Semaphore --> 0
release lock --> Thread-2 (test)
Value Of Semaphore --> 1
acquired lock --> Thread-3 (test)
Value Of Semaphore --> 0
release lock --> Thread-3 (test)
Value Of Semaphore --> 1

If you were to change the value of the count from 1 to 2, you would allow two threads to the restricted code at a time. So, the output will be different.

Value Of Semaphore --> 2
acquired lock --> Thread-1 (test)
Value Of Semaphore --> 1
Value Of Semaphore --> 1
release lock --> Thread-1 (test)
Value Of Semaphore --> 1
Value Of Semaphore --> 1
acquired lock --> Thread-3 (test)
acquired lock --> Thread-2 (test)
Value Of Semaphore --> 0
Value Of Semaphore --> 0
release lock --> Thread-2 (test)
release lock --> Thread-3 (test)
Value Of Semaphore --> 1
Value Of Semaphore --> 2

Notice that the time it takes for the semaphore to release the thread is based on the speed of your device, and every time, it’ll be different. You can test the above code by changing the value of the count in the instance of the semaphore.

Author: Haider Ali
Haider Ali avatar Haider Ali avatar

Haider specializes in technical writing. He has a solid background in computer science that allows him to create engaging, original, and compelling technical tutorials. In his free time, he enjoys adding new skills to his repertoire and watching Netflix.

LinkedIn