Как очереди создают потоки в Python

Я признаю, что это было задано мне в интервью давным-давно, но я никогда не удосужился его проверить.

Вопрос прост, как Python делает Queue потокобезопасным?

Мой ответ был из-за Interpreter Lock (GIL), в любое время только один поток делает вызов, чтобы получить элемент из очереди, в то время как другие спят /ждут. Я /до сих пор не уверен, что это правильный ответ.

Интервьюер казался недоволен и спросил , если Queue s являются потокобезопасными в Java или .NET-реализации Python, который не имеет GIL? Если да, то каким образом они реализуют потокобезопасную функцию в указанной структуре данных.

Я пробовал искать его, но я всегда натыкаюсь на how to use thread-safe queues.

Итак, как Queue или простой list можно сделать потокобезопасным и избежать условий гонки?

В качестве альтернативы, какие алгоритмы или методы, используемые в реализации GEvent реализации потокобезопасного Queue s

3 голоса | спросил Harsh Gupta 2 FebruaryEurope/MoscowbFri, 02 Feb 2018 15:31:37 +0300000000pmFri, 02 Feb 2018 15:31:37 +030018 2018, 15:31:37

1 ответ


3

Вы ошибаетесь, что GIL сделает программу Python потокобезопасной. Это делает сам интерпретатор потокобезопасным.

Например, давайте посмотрим на суперпростую очередь LIFO (aka. a Stack). Мы проигнорируем, что list уже может использоваться как стек.

 class Stack(object):
  def __init__(self, capacity):
    self.size = 0
    self.storage = [None] * capacity

  def push(self, value):
    self.storage[self.size] = value
    self.size += 1

  def pop(self):
    self.size -= 1
    result = self.storage[self.size]
    self.storage[self.size] = None
    return result

Является ли это потокобезопасным? Абсолютно нет, несмотря на бегство под GIL.

Рассмотрим эту последовательность событий:

  • В потоке 1 добавлено несколько значений

     stack = Stack(5)
    stack.push(1)
    stack.push(2)
    stack.push(3)
    

    Теперь состояние storage=[1, 2, 3, None, None], size=3.

  • В потоке 1 добавляется значение stack.push(4) и приостанавливается до того, как размер может быть увеличен

     self.storage[self.size] = value
    # interrupted here
    self.size += 1
    

    Теперь состояние storage=[1, 2, 3, 4, None], size=3.

  • В потоке 2 удаляется значение stack.pop(), которое является 3.

    Теперь состояние storage=[1, 2, None, 4, None], size=2.

  • Вопрос 1 возобновляется

     self.storage[self.size] = value
    # resume here
    self.size += 1
    

    Теперь состояние storage=[1, 2, None, 4, None], size=3.

В результате, поврежден стек: вытолкнутое значение не может быть извлечено, а верхний элемент пуст.

GIL только линеаризует доступ к данным, но это почти совершенно бесполезно для обычного разработчика Python, потому что порядок операций по-прежнему непредсказуем. То есть GIL не может использоваться как блокировка на уровне Python, он просто гарантирует, что значения всех переменных обновлены (volatile в C или Java). Реализации Python без GIL должны также предоставлять это свойство для совместимости, например. используя volatile память или использует свои собственные блокировки. Jython - это реализация без GIL , которая специально использует поточно-реалистичные реализации для ---- +: = 18 =: + ----, dict и т. д.

Поскольку Python не гарантирует никакого порядка операций между потоками, неудивительно, что поточно-безопасные структуры данных должны использовать блокировку. Например, стандартная библиотека list class @ v3.6.4 имеет член queue.Queue и несколько condvars, использующих этот мьютекс. Все обращения к данным должным образом охраняются. Но обратите внимание, что этот класс не предназначен прежде всего для структуры данных очереди, а как очередь заданий между несколькими потоками. Чистая структура данных обычно не связана с блокировкой.

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

ответил amon 3 FebruaryEurope/MoscowbSat, 03 Feb 2018 00:22:00 +0300000000amSat, 03 Feb 2018 00:22:00 +030018 2018, 00:22:00

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

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132