Для чего нужен lock в python? Как работает данный пример кода?

Lock (блокировка) в Python — это механизм синхронизации, который позволяет ограничивать доступ к определенным участкам кода только одному потоку одновременно. Он предотвращает гонки за данными и обеспечивает атомарность операций, что помогает избежать неопределенного поведения или ошибок в многопоточном программировании.

Пример кода ниже демонстрирует использование Lock:

import threading

counter = 0
lock = threading.Lock()

def increment():
    global counter
    for _ in range(1000000):
        lock.acquire() # захватываем блокировку
        counter += 1
        lock.release() # освобождаем блокировку

def decrement():
    global counter
    for _ in range(1000000):
        lock.acquire()
        counter -= 1
        lock.release()

t1 = threading.Thread(target=increment)
t2 = threading.Thread(target=decrement)

t1.start()
t2.start()

t1.join()
t2.join()

print(counter)

В приведенном коде определены две функции: increment() и decrement(). Обе функции выполняются в отдельных потоках и увеличивают или уменьшают значение переменной counter на 1 миллион раз соответственно.

Основной объект блокировки, lock, создается из класса threading.Lock(). Блокировка получается вызовом метода acquire() и освобождается вызовом метода release().

Когда один поток приходит к lock.acquire(), он захватывает блокировку, и следующий поток, который придет к этому вызову, будет ждать, пока блокировка не будет освобождена. Это гарантирует, что только один поток одновременно имеет доступ к критическому участку кода, в данном случае операции увеличения/уменьшения counter. Когда поток завершает операцию увеличения или уменьшения counter, он вызывает lock.release(), чтобы освободить блокировку и передать ее следующему потоку в очереди.

После создания и запуска двух потоков t1 и t2, в основном потоке вызывается t1.join() и t2.join(), чтобы убедиться, что оба потока выполнились до конца, прежде чем вывести значение переменной counter.

Таким образом, блокировка в данном примере обеспечивает правильное и синхронное обновление значений переменной counter в многопоточной среде, предотвращая возможные гонки за данными.