Synchronization Between Threads
Synchronization Between Threads
Critical section refers to the parts of the program where the shared resource
is accessed.
A race condition occurs when two or more threads can access shared data
and they try to change it at the same time. As a result, the values of
variables may be unpredictable and vary depending on the timings of
context switches of the processes.
Iteration 0: x = 175005
Iteration 1: x = 200000
Iteration 2: x = 200000
Iteration 3: x = 169432
Iteration 4: x = 153316
Iteration 5: x = 200000
Iteration 6: x = 167322
Iteration 7: x = 200000
Iteration 8: x = 169917
Iteration 9: x = 153589
In above program:
Two threads t1 and t2 are created in main_task function and global
variable x is set to 0.
Given below is a diagram which shows how can race condition occur in
above program:
Using Locks
threading module provides a Lock class to deal with the race conditions.
Lock is implemented using a Semaphore object provided by the Operating
System.
A semaphore is a synchronization object that controls access by multiple
processes/threads to a common resource in a parallel programming
environment. It is simply a value in a designated place in operating system (or
kernel) storage that each process/thread can check and then change.
Depending on the value that is found, the process/thread can use the
resource or will find that it is already in use and must wait for some period
before trying again. Semaphores can be binary (0 or 1) or can have additional
values. Typically, a process/thread using semaphores checks the value and
then, if it using the resource, changes the value to reflect this so that
subsequent semaphore users will know to wait.
Iteration 0: x = 200000
Iteration 1: x = 200000
Iteration 2: x = 200000
Iteration 3: x = 200000
Iteration 4: x = 200000
Iteration 5: x = 200000
Iteration 6: x = 200000
Iteration 7: x = 200000
Iteration 8: x = 200000
Iteration 9: x = 200000
lock = threading.Lock()
t1 = threading.Thread(target=thread_task, args=(lock,))
t2 = threading.Thread(target=thread_task, args=(lock,))
lock.acquire()
increment()
lock.release()
As you can see in the results, the final value of x comes out to be 200000
every time (which is the expected final result).
Here is a diagram given below which depicts the implementation of locks in
above program:
Advantages:
Disadvantages: