Потоки Python - критический раздел

Расширение PDE от Andrew Richards добавляет команду !busy

  !busy          - Equivalent of ~*knL but only displays stacks that are at least
                   'depth' frames deep (default depth is 1) and are not waiting for:-
                    ~ ntdll!NtWaitFor*
                    ~ ntdll!ZwWaitFor*
                    ~ ntdll!NtRemoveIoCompletion
                    ~ ntdll!ZwRemoveIoCompletion
                    ~ ntdll!NtReplyWaitReceivePort
                    ~ ntdll!ZwReplyWaitReceivePortEx

Проблематично найти для нее загрузку, но вы можете пропинговать Andrew по каналу Channel 9. Он обычно отвечает довольно быстро.

12
задан cdleary 7 January 2009 в 18:15
поделиться

3 ответа

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

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

17
ответ дан 2 December 2019 в 04:34
поделиться

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

0
ответ дан 2 December 2019 в 04:34
поделиться

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

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()
  • на месте продажи: считайте текущее значение-> 0
  • отрицательный: считайте текущее значение-> 0
  • на месте продажи: добавьте текущее значение для чтения значения-> 100
  • отрицательный: добавьте текущее значение для чтения значения->-100
  • на месте продажи: запишите текущее значение-> account_balance = 100
  • отрицательный: запишите текущее значение-> 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
ответ дан 2 December 2019 в 04:34
поделиться
Другие вопросы по тегам:

Похожие вопросы: