Python Threads-Критический Раздел

Что такое "критический раздел" потока (в Python)?

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

- понимание потоков в Python, Linux Gazette

кроме того, какова цель блокировки?

3 ответов


критический раздел кода-это тот, который может выполняться только одним потоком за раз. Возьмем, к примеру, чат-сервер. Если у вас есть поток для каждого соединения (т. е. каждого конечного пользователя), один "критический раздел" - это код буферизации (отправка входящего сообщения всем клиентам). Если несколько нитей попытаются катить сообщение одновременно, вы получите переплетение Bfritos Mantwd PIoEmesCEsaSges, что, очевидно, совсем не хорошо.

замок-это то, что можно использовать для синхронизировать доступ к критическому разделу (или ресурсам в целом). В нашем примере сервера чата блокировка похожа на запертую комнату с пишущей машинкой. Если один поток находится там (для ввода сообщения), никакой другой поток не может попасть в комнату. Как только первая нить закончена, он отпирает комнату и уходит. Затем еще одна нить может идти в комнату (запирая ее). Замок означает "я получаю комнату"."


другие люди дали очень хорошие определения. Вот классический пример:

import threading
account_balance = 0 # The "resource" that zenazn mentions.
account_balance_lock = threading.Lock()

def change_account_balance(delta):
    global account_balance
    with account_balance_lock:
        # Critical section is within this block.
        account_balance += delta

скажем,+= оператор состоит из трех подкомпонентов:

  • читать текущее значение
  • добавьте RHS к этому значению
  • запишите накопленное значение обратно в LHS (технически bind это в терминах Python)

если у вас нет with account_balance_lock заявление и вы выполняете два change_account_balance звонки параллельно вы можете в конечном итоге чередования трех операций подкомпонент в опасной манере. Допустим, вы одновременно звоните change_account_balance(100) (он же pos) и change_account_balance(-100) (он же neg). Это могло случиться:

pos = threading.Thread(target=change_account_balance, args=[100])
neg = threading.Thread(target=change_account_balance, args=[-100])
pos.start(), neg.start()
  • pos: читать текущее значение - > 0
  • neg: читать текущее значение - > 0
  • pos: добавить текущее значение для чтения значения - > 100
  • neg: добавить текущее значение для чтения значения -> -100
  • pos: записать текущее значение -> account_balance = 100
  • neg: записать текущее значение - > account_balance = -100

поскольку вы не заставляли операции происходить в дискретных кусках, вы можете иметь три возможных результата (-100, 0, 100).

на with [lock] оператор-это одна неделимая операция, которая говорит: "Позвольте мне быть единственным потоком, выполняющим этот блок кода. Если что-то еще исполняется, это круто-я подожду."Это гарантирует, что обновления account_balance несколько "потокобезопасный" (parallelism-safe).

Примечание: есть оговорка к этой схеме: вы должны помнить, чтобы получить account_balance_lock (via with) каждый раз, когда вы хотите манипулировать account_balance чтобы код оставался потокобезопасным. Есть способы сделать это менее хрупким, но это ответ на совершенно другой вопрос.

Edit: в ретроспективе, вероятно, важно упомянуть, что with оператор неявно вызывает блокировку acquire on the lock -- это часть" я подожду " диалогового окна выше потока. Напротив, неблокирующее приобретение говорит:" Если я не могу получить замок сразу, дайте мне знать", а затем полагается на вас, чтобы проверить, получили ли вы замок или нет.

import logging # This module is thread safe.
import threading

LOCK = threading.Lock()

def run():
    if LOCK.acquire(False): # Non-blocking -- return whether we got it
        logging.info('Got the lock!')
        LOCK.release()
    else:
        logging.info("Couldn't get the lock. Maybe next time")

logging.basicConfig(level=logging.INFO)
threads = [threading.Thread(target=run) for i in range(100)]
for thread in threads:
   thread.start()

я также хочу добавить, что основная цель замка-гарантировать атомарность приобретения (неделимость acquire через потоки), который простой логический флаг не гарантирует. Семантика атомарного операции возможно также содержание другой вопрос.


A "критическую секцию" - Это фрагмент кода, в котором для корректности необходимо убедиться, что в этом разделе может находиться только один поток управления. В общем, вам нужен критический раздел, чтобы содержать ссылки, которые написать значения в память, которые могут быть разделены между более чем одним параллельным процессом.