Приходилось ли писать очередь для передачи данных между тредами самостоятельно
Что проверяют
Понимаете ли вы, зачем очередь нужна между потоками, и знаете ли, что в стандартной библиотеке она уже есть. Ответ отделяет того, кто умеет пользоваться queue.Queue, от того, кто пишет своё на списке и блокировке, не зная цены такого решения.
Как отвечать
Честно: своё почти никогда не пишут. queue.Queue потокобезопасна, внутри у неё deque и условные переменные, потребитель спит, пока данных нет. Варианты под задачу: Queue это FIFO, рядом есть LifoQueue, PriorityQueue и облегчённая SimpleQueue.
Дальше стоит показать эксплуатационную часть:
maxsizeэто обратное давление: без ограничения быстрый производитель раздувает память;- завершение через маркер, по одному на каждого потребителя, либо
get(timeout=...)и флаг остановки; task_done()вfinally, иначе исключение в обработчике подвеситjoin()навсегда.
q = queue.Queue(maxsize=1000)
def worker():
while True:
item = q.get()
try:
if item is STOP:
return
handle(item)
finally:
q.task_done()Отдельно назовите границы применимости: между процессами нужна multiprocessing.Queue или внешний брокер, а asyncio.Queue не потокобезопасна, класть в неё из другого потока можно только через loop.call_soon_threadsafe.
Где спотыкаются
Пишут свою очередь на списке с блокировкой и опросом в цикле. У списка pop(0) линейный по длине, а активное ожидание жжёт процессор на пустой очереди вместо того, чтобы спать на условной переменной.
Второе: ждут от потоков ускорения вычислений. Из-за глобальной блокировки интерпретатора потоки выигрывают на ожидании ввода-вывода, а счётную нагрузку выносят в отдельные процессы.
Третье: пересказывают историю про рост производительности и снижение задержек без единого замера. Уточняющий вопрос про то, чем мерили и какой была глубина очереди, такую историю сразу разваливает.