Поток - это блок кода, который выполняется независимо. Корутина - это как правило набор блоков, связанных между собой. У задачи в потоке как правило нет колбэка, поэтому чтобы получить результат, его надо где-то подождать. Простая корутина типа launch без suspension point внутри похожа на поток, но у нее всегда есть callback. В общем случае корутины дают граф задач с инвертированной осью времени.
Но это лишь детали реализации одной и той же задачи.
Для упрощения будем иметь ввиду один процессор.
Если вы запускаете поток, то создаётся новый стек вызовов и там вы исполняете свой код.
Как все знают, на одном ядре OS менеджмент ваши потоки и быстро между ними переключается, давая время поработать коду из другого потока.
Корутина это тоже блок кода, только вы не создаёте новый стек вызовов, а используете текущий (без переключения контекста). И ОС не шедулит выполнение, а вы делаете это сами. Вызов колбэка возвращает вас в то место стека вызовов где вы запустили корутину.
Да, работают они по разному, но решают точно одну и ту же задачу