Язык программирования Python
Шрифт:
строго упорядочить все ресурсы (например, по цене) и всегда запрашивать их в определенном порядке (скажем, начиная с более дорогих ресурсов). При этом перед заказом некоторого ресурса поток должен отказаться от заблокированных им более дешевых ресурсов.
Семафоры
Семафоры (их иногда называют семафорами Дийкстры (Dijkstra) по имени их изобретателя) являются более общим механизмом синхронизации потоков, нежели замки. Семафоры могут допустить в критическую область программы сразу несколько потоков. Семафор имеет счетчик запросов,
Конструктор класса threading.Semaphore принимает в качестве (необязательного) аргумента начальное состояние счетчика (по умолчанию оно равно 1, что соответствует замку класса Lock). Методы acquire и release действуют аналогично описанным выше одноименным методам у замков.
Семафор может применяться для охраны ограниченного ресурса. Например, с его помощью можно вести пул соединений с базой данных. Пример такого использования семафора (заимствован из документации к Python) дан ниже:
Листинг
from threading import BoundedSemaphore
maxconnections = 5
# Подготовка семафора
pool_sema = BoundedSemaphore(value=maxconnections)
# Внутри потока:
pool_sema.acquire
conn = connectdb
# … использование соединения …
conn.close
pool_sema.release
Таким образом, применяется не более пяти соединений с базой данных. В примере использован класс threading.BoundedSemaphore. Экземпляры этого класса отличаются от экземпляров класса threading.Semaphore тем, что не дают сделать release больше, чем сделан acquire.
События
Еще одним способом коммуникации между объектами являются события. Экземпляры класса threading.Event могут быть использованы для передачи информации о наступлении некоторого события от одного потока одному или нескольким другим потокам. Объекты–события имеют внутренний флаг, который может находиться в установленном или сброшенном состоянии. При своем создании флаг события находится в сброшенном состоянии. Если флаг в установленном состоянии, ожидания не происходит: поток, вызвавший метод wait для ожидания события, просто продолжает свою работу. Ниже приведены методы экземпляров класса threading.Event:
setУстанавливает внутренний флаг, сигнализирующий о наступлении события. Все ждущие данного события потоки выходят из состояния ожидания.
clearСбрасывает флаг. Все события, которые вызывают метод wait этого объекта–события, будут находиться в состоянии ожидания до тех пор, пока флаг сброшен, или по истечении заданного таймаута.
isSetВозвращает состояние флага.
wait([timeout])Переводит поток в состояние ожидания, если флаг сброшен, и сразу возвращается, если флаг установлен. Аргумент timeout задает таймаут в секундах,
Составить пример работы с событиями предлагается в качестве упражнения.
Условия
Более сложным механизмом коммуникации между потоками является механизм условий. Условия представляются в виде экземпляров класса threading.Condition и, подобно только что рассмотренным событиям, оповещают потоки об изменении некоторого состояния. Конструктор класса threading.Condition принимает необязательный параметр, задающий замок класса threading.Lock или threading.RLock. По умолчанию создается новый экземпляр замка класса threading.RLock. Методы объекта–условия описаны ниже:
acquire(…)Запрашивает замок. Фактически вызывается одноименный метод принадлежащего объекту–условию объекта–замка.
releaseСнимает замок.
wait([timeout])Переводит поток в режим ожидания. Этот метод может быть вызван только в том случае, если вызывающий его поток получил замок. Метод снимает замок и блокирует поток до появления объявлений, то есть вызовов методов notify и notifyAll другими потоками. Необязательный аргумент timeout задает таймаут ожидания в секундах. При выходе из ожидания поток снова запрашивает замок и возвращается из метода wait.
notifyВыводит из режима ожидания один из потоков, ожидающих данные условия. Метод можно вызвать, только овладев замком, ассоциированным с условием. Документация предупреждает, что в будущих реализациях модуля из целей оптимизации этот метод будет прерывать ожидание сразу нескольких потоков. Сам по себе метод notify не приводит к продолжению выполнения ожидавших условия потоков, так как этому препятствует занятый замок. Потоки получают управление только после снятия замка потоком, вызвавшим метод notify.
notifyAllЭтот метод аналогичен методу notify, но прерывает ожидание всех ждущих выполнения условия потоков.
В следующем примере условия используются для оповещения потоков о прибытии новой порции данных (организуется связь производитель — потребитель, producer — consumer):
Листинг
import threading
cv = threading.Condition
class Item:
«"«Класс–контейнер для элементов, которые будут потребляться
в потоках»""
def __init__(self):
self._items = []
def is_available(self):
return len(self._items) > 0
def get(self):
return self._items.pop
def make(self, i):
self._items.append(i)
item = Item
def consume:
«"«Потребление очередного элемента (с ожиданием его появления)»""
cv.acquire
while not item.is_available:
cv.wait
it = item.get
cv.release
return it
def consumer: