Что такое параллельность и конкурентность и в чем их разница?
Что проверяют
Различаете ли вы устройство программы и способ её исполнения, и знаете ли, как это ложится на CPython с его глобальной блокировкой интерпретатора. Ответ питониста без упоминания GIL считается неполным.
Как отвечать
Конкурентность это про структуру: задачи разбиты так, что могут продвигаться с чередованием, по очереди уступая друг другу. Параллельность это про исполнение: несколько задач считаются физически одновременно на разных ядрах. Конкурентная программа прекрасно живёт на одном ядре, параллельная требует нескольких.
Дальше привязка к Python. GIL разрешает исполнять байткод только одному потоку за раз, поэтому потоки в CPython дают конкурентность, но не ускоряют счётную нагрузку. Блокировка отпускается на время операций ввода и вывода и внутри C-расширений, поэтому потоки отлично разгоняют работу с сетью, диском и вычисления в numpy.
Отсюда выбор инструмента. asyncio: конкурентность в одном потоке, кооперативное переключение на await, хорош для тысяч сетевых соединений. threading и ThreadPoolExecutor: конкурентность с вытеснением, годятся для блокирующего ввода и вывода и для библиотек без асинхронного интерфейса. multiprocessing и ProcessPoolExecutor: настоящая параллельность, отдельные интерпретаторы, цена в запуске процессов и сериализации аргументов.
Уместно добавить, что в современных версиях CPython развивается сборка без GIL, но по умолчанию правило выше пока действует.
Где спотыкаются
Называют пул потоков примером параллельности. На счётной нагрузке он не только не ускорит, но и замедлит из-за борьбы потоков за блокировку.
Смешивают блокирующий код с asyncio. Обычный sleep или синхронный клиент внутри корутины останавливают весь цикл событий целиком, нужен асинхронный аналог или вынос вызова в исполнителя.
Считают процессы бесплатной заменой потокам. Аргументы и результаты проходят через pickle, общей памяти нет, и на мелких задачах накладные расходы съедают весь выигрыш.