Темы Python - Критический раздел - PullRequest
12 голосов
/ 07 января 2009

Что такое «критическая секция» потока (в Python)?

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

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

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

Ответы [ 3 ]

16 голосов
/ 07 января 2009

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

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 (технически связать это в терминах Python)

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

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 являются «поточно-ориентированными» (параллелизм-безопасными).

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

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

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 между потоками), что не гарантирует простой логический флаг. Семантика атомарных операций, вероятно, также является содержанием другого вопроса.

16 голосов
/ 07 января 2009

Критическим разделом кода является тот, который может выполняться только одним потоком за раз. Возьмите, например, сервер чата. Если у вас есть поток для каждого соединения (т. Е. Каждого конечного пользователя), одним «критическим разделом» является код буферизации (отправка входящего сообщения всем клиентам). Если более чем один поток пытается спулингировать сообщение одновременно, вы получите перепутанный BfrIToS MANTWD PIoEmesCEsaSges, что, очевидно, не очень хорошо.

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

0 голосов
/ 07 января 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...