Python - один из языков программирования, который поддерживает многопроцессорность и многопоточность, и это довольно круто.
В конце концов, python не является одним из лучших для параллельного программирования, но давайте посмотрим, как мы можем использовать и решить общую проблему: условия гонки
Прежде чем я покажу вам пример, сначала мы должны познакомиться с этой концепцией: взаимное исключение, блокировка, условия гонки.
Взаимное исключение: когда два или более процессов / потоков хотят получить доступ к общему ресурсу, и после того, как поток получил доступ к этому ресурсу, они блокируют все остальные одновременно.
Блокировка: поток перед доступом к общему ресурсу, должен построить график или получить блокировку, на этом этапе все другие параллельные участники ждут снятия блокировки. Блокировка снимается после того, как поток завершил время своего выполнения с использованием этого ресурса.
Условия гонки: для этого используется взаимное исключение, чтобы разрешить условия гонки.
Состояние гонки возникает, когда два или более параллельных потока одновременно обращаются к общему ресурсу и обновляют его, то есть он не защищен блокировкой.
from threading import Thread,Lock x = 1 # Shared resource def fibonaci(n): if n<=2 : return 1 return fibonaci(n-1) + fibonaci(n-2) def firstFibonaci(): global x print("firstFibonaci resource is {} \n".format(x) ) while x <= 10: print(fibonaci(x)) x+=1 def secondFibonaci(): global x print("secondFibonaci resource is {} \n".format(x) ) while x <= 10: print(fibonaci(x)) x+=1 Parallel_1 = Thread(target=firstFibonaci) Parallel_2 = Thread(target=secondFibonaci) Parallel_1.start() Parallel_2.start() Parallel_1.join() Parallel_2.join()
Я создал два метода firstFibonaci и secondFibonaci, и для каждого из них общий ресурс x находится в глобальной области видимости. Общий ресурс - x, и потоки к нему будут обращаться параллельно.
Технически второй метод не должен обновлять общий ресурс, поскольку он увеличивается до 11 методом firstFibonaci, но так ли это?
Это ожидаемый правильный ответ:
$ /usr/bin/python race_condition.py firstFibonaci resource is 1 1 1 2 3 5 8 13 21 34 55 secondFibonaci resource is 11
Но посмотрите, как на самом деле генерируется ответ:
$ /usr/bin/python race_condition.py firstFibonaci resource is 1 1 secondFibonaci resource is 1 1 12 35 8 13 34 2155
и другой :
$ /usr/bin/python race_condition.py firstFibonaci resource is 1 1 1 2 3 5 secondFibonaci resource is 6 88 13 2134 55
Почему так случилось?
Это потому, что общий ресурс x не защищен блокировкой. Буксируемые потоки одновременно получили доступ к этому ресурсу, обновив состояние. Теперь, чтобы разрешить это состояние гонки, мы перейдем к другому классу потоковой передачи пакетов, а именно Lock.
Этот класс имеет основной метод буксировки:
получить: Это означает, что блокировка отображается потоком, и все параллельные операции блокируются в ожидании освобождения.
release: Это означает, что блокировка свободна и готова для другого потока.
Посмотрите, как это работает.
from threading import Thread,Lock x = 1 #Shared resource #Initialize an instance of object Lock lock_race = Lock() def fibonaci(n): if n<=2 : return 1 return fibonaci(n-1) + fibonaci(n-2) def firstFibonaci(): global x try: lock_race.acquire() # Graph a lock print("firstFibonaci resource is {} \n".format(x) ) while x <= 10: print(fibonaci(x)) x+=1 finally: # Release the lock, now it is free, and can be graphed by # the second thread lock_race.release() def secondFibonaci(): global x try: lock_race.acquire() print("secondFibonaci resource is {} \n".format(x) ) while x <= 10: print(fibonaci(x)) x+=1 finally: lock_race.release() Parallel_1 = Thread(target=firstFibonaci) Parallel_2 = Thread(target=secondFibonaci) Parallel_1.start() Parallel_2.start() Parallel_1.join() Parallel_2.join()
Теперь у нас должен быть правильный и исключенный ответ:
$ /usr/bin/python race_condition.py firstFibonaci resource is 1 1 1 2 3 5 8 13 21 34 55 secondFibonaci resource is 11
Как мы видим, это сработало очень хорошо, и к общему ресурсу сначала обращается первый параллельный поток, который изобразил блокировку, блокирующую других участников, таким образом, общий ресурс x Не дорабатывалась при этом буксировкой разных потоков.
Я просто хочу кое-что прояснить, как можно использовать блокировку:
lock_race = Lock() try: lock_race.acquire() finally: lock_race.release() # Is the same as with lock_race: pass
Заключение
Проблема с условиями гонки может привести к катастрофическим последствиям для кода, поэтому параллельное выполнение потоков следует использовать с осторожностью. Библиотеки Python, такие как threading, lock, могут помочь нам решить эту проблему.
Спасибо