Skip to content

Instantly share code, notes, and snippets.

@imgVOID
Last active September 11, 2021 15:32
Show Gist options
  • Save imgVOID/741e1da679ddb11247076eb62b630081 to your computer and use it in GitHub Desktop.
Save imgVOID/741e1da679ddb11247076eb62b630081 to your computer and use it in GitHub Desktop.
Пример решения задачи consumer-producer (задача ограниченного буфера) на Python с использованием только yield и next(). Каждая строчка кода прокомментирована объяснениями, функции задокументированы. Пример создан для курса Python Django Developer.
"""
Сопрограмма (англ. coroutine) — компонент программы, обобщающий понятие
подпрограммы, который дополнительно поддерживает множество входных точек
(а не одну, как подпрограмма), и остановку/продолжение выполнения с
сохранением определённого состояния.
Здесь показан пример такого шаблона использования сопрограмм, как
consumer-producer (потребитель-поставщик).
Суть задачи в том, чтобы одна сопрограмма поставляла объекты в буфер,
а другая - обрабатывала по одному объекту из буфера за раз.
Главное - не позволить поставщику сгенерировать слишком много данных,
а потребителю не дать удалить несуществущие данные из пустого буфера.
"""
import time
import random
def sleep(seconds):
"""
Сопрограмма, которая приостанавливает сопрограмму,
из которой была вызвана, на заданное количество секунд
"""
initial_time = time.time()
# Пока время старта программы отличается от текущего
# времени меньше, чем задано - повторять ожидание
while time.time() - initial_time < seconds:
yield
def consume(buffer):
"""
Сопрограмма обработки данных (потребитель)
"""
while True:
try:
# Получение набора данных для обработки
data = buffer.pop(0)
except IndexError:
# Буфер пуст, данные не обрабатываются.
print('Buffer is empty.\n')
else:
# Создание кортежа из полученного
# генератора случайных чисел (данные).
# Только здесь действительно вычисляются случайные числа.
data = tuple(data)
# Вывод на экран значений и суммы этих значений
print(f'Got data: {", ".join(str(x) for x in data)}\n'
f'Data sum: {sum(data)}\n')
finally:
# Пауза на 0.7 секунды.
yield from sleep(0.7)
def produce(buffer, max_buffer):
"""
Сопрограмма выдачи данных (поставщик)
"""
def gen_data():
"""
Функция, возвращающая генератор случайных чисел
"""
return (random.randint(1, 5) for _
in range(0, random.randint(2, 4)))
while True:
# С некоторой случайной вероятностью, когда в буфере есть место,
# генерируются новые данные.
if len(buffer) < max_buffer and random.randint(0, 1):
# Добавление данных в буфер
buffer.extend([gen_data()])
# Пауза на случайное время в пределах 0.6-1 секунды.
yield from sleep(random.uniform(0.6, 1))
def main():
max_buffer = 4
# Общий буфер
buffer = []
# Создание обработчика данных
consumer = consume(buffer)
# Создание производителя данных
producer = produce(buffer, max_buffer)
# Цикл событий (event loop)
while True:
# Запуск сопрограмм
next(producer)
next(consumer)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment