Last active
September 11, 2021 15:32
-
-
Save imgVOID/741e1da679ddb11247076eb62b630081 to your computer and use it in GitHub Desktop.
Пример решения задачи consumer-producer (задача ограниченного буфера) на Python с использованием только yield и next(). Каждая строчка кода прокомментирована объяснениями, функции задокументированы. Пример создан для курса Python Django Developer.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Сопрограмма (англ. 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