Потоки имеют смысл, когда тебе нужно быстро и дёшево выделить синхронный код в отдельную единицу исполнения без заморочек межпроцессной коммуникации. Иными словами:
- если выносимый код исполняется в течении длительного времени
- если нет возможности или желания переделывать выносимый код в корутину, а хочется оставить вытесняющую многозадачность
- если обмен данными с этой корутиной достаточно сложен, чтобы сериализация/десериализация не окупалась (хотя shared memory может помочь).
- если код не нагружен вычислениями (в связи с GIL)
В общем, потоки - это простое и ленивое решение.
Пример раз: асинхронное приложение, но с GUI без поддержки асинхронщины. GUI требует рабочий цикл, asyncio требует цикл-реактор, подружить их в одном потоке трудно, а вытащить GUI в отдельный процесс может потребовать заморочек с организацией взаимодействия. Проще запустить GUI-цикл в отдельном потоке и работать с ним там. (Хотя иногда есть хаки, позволяющие обновлять GUI в рамках "while True:" корутины.)
Пример два: асинхронная работа с файлами. Насколько я знаю, в питоне файлы чисто синхронные, а библиотеки для асинхронного доступа (типа aiofiles) под виндой "жульничают", выполняя синхронные операции как раз в отдельном потоке.
Да и в целом, если нужно выполнить долгий синхронный код в асинхронном приложении, вынос его в отдельный поток может оказаться наиболее удобным решением.