Thread synchronization is defined as a mechanism , It ensures that two or more concurrent threads do not execute something called Key segment Specific segments of .
The key part is the part of the program that accesses the shared resources .
for example , In the following illustration ,3 Threads trying to access shared resources or critical parts at the same time .
Concurrent access to shared resources can lead to Contention .
When two or more threads can access the shared data and try to change the shared data at the same time , Contention will occur . therefore , The value of a variable may be unpredictable , And it changes according to the context switching time of the process .
Consider the following procedure to understand the concept of contention conditions :
import threading
# Global variables x
x = 0
def increment():
""" Used to increment global variables x Function of """
global x
x += 1
def thread_task():
""" The task of the thread calls the incremental function 100000 Time ."""
for _ in range(10000000):
increment()
def main_task():
global x
# Put the global variable x Set to 0
x = 0
# Create thread
t1 = threading.Thread(target=thread_task)
t2 = threading.Thread(target=thread_task)
# Open thread
t1.start()
t2.start()
# Wait for the thread to complete
t1.join()
t2.join()
if __name__ == "__main__":
for i in range(10):
main_task()
print(" iteration {0}: x = {1}".format(i, x))
Running results :
In the above procedure :
x The expected final value of is 200000, But we are in the function 10 Sub iteration Get in main_task Are some different values .
( If you get the same result every time you run , It may be because your computer has good performance , Try this to enlarge
thread_task()
Method data , If one or more zeros are added , Such as 10000000 Time )
This happens because the thread shares variables x Concurrent access to .x This unpredictability of values is nothing more than Race condition .
Here is a chart , Shows how this happens in the above program Contention condition :
Please note that , Above picture x Expected value for 12, But because of the contention condition , The result is 11!
therefore , We need a tool to properly synchronize multiple threads .
Here we will use thread lock
Threads The module provides one Lock Class to handle contention conditions . Locking is provided by the operating system Semaphore Object implemented .
A semaphore is a synchronization object , Used to control multiple processes / Thread access to common resources in parallel programming environment . It's just the operating system ( Or kernel ) The value of the specified location in the storage , Each process / Threads can check the value , Then make changes . Based on the value found , process / Threads can use this resource , Or you may find it already in use , And you have to wait a while to try again . Semaphores can be binary (0 or 1), You can also have other values . Usually , Processes that use semaphores / The thread will check the value , then , If it uses resources , Then change the value to reflect this value , So that subsequent semaphore users will know to wait .
Lock Class provides the following methods :
obtain ([ Blocking ]) : Get the lock . Locks can be blocked , It can also be non blocking .
Release () : Release the lock .
Consider the example given below :
import threading
# Global variables x
x = 0
def increment():
""" Used to increment global variables x Function of """
global x
x += 1
def thread_task(lock):
""" The task of the thread calls the incremental function 100000 Time ."""
for _ in range(100000):
lock.acquire()
increment()
lock.release()
def main_task():
global x
# Set the global variable to 0
x = 0
# Create thread lock
lock = threading.Lock()
# Create thread
t1 = threading.Thread(target=thread_task, args=(lock,))
t2 = threading.Thread(target=thread_task, args=(lock,))
# Open thread
t1.start()
t2.start()
# Wait for all threads to finish
t1.join()
t2.join()
if __name__ == "__main__":
for i in range(10):
main_task()
print(" iteration {0}: x = {1}".format(i, x))
Running results :
Let's try to understand the above code step by step :
First , Use the following command to create Lock object :
lock = threading.Lock()
then , take lock Pass as an objective function parameter :
t1 = threading.Thread(target=thread_task, args=(lock,))
t2 = threading.Thread(target=thread_task, args=(lock,))
In the key part of the objective function , We use lock.acquire() Method application lock. Once you get the lock , In the use of lock.release() Method before releasing the lock , No other thread has access to critical parts ( Here is The incremental function ).
lock.acquire()
increment()
lock.release()
As you can see in the results ,x The final value of is displayed as 200000( This is the expected end result ).
Here is a chart , The implementation of lock in the above program is described :
Last , Here are some of the advantages and disadvantages of multithreading :
advantage :
Disadvantages :