понимаю некоторые вещи. Вот когда мы запускаем корутину, т.е. вызываем какую-то suspend функцию, насколько я понимаю, во время компиляции из нее генерируется стейт-машина, где самый маленький execution unit - это код между двумя suspend вызовами, и если результат suspend-вызова еще не готов, то возвращаемся(сохраняя стейт, конечно). Но я не совсем понимаю, а кто заново вернется в эту функцию, когда результат suspend-функции будет готов? Там есть какой-то event-loop параллельно? И вообще, насколько я далек от правильного понимания?
Да, в целом так. Корутины оборачиваются в таски и закидываются в executor, после исполнения которой, он опять закидывает таску с вызовом resume на корутине (вроде). Я детали подзабыл. https://stackoverflow.com/questions/53526556/how-do-kotlin-coroutines-work-internally https://kt.academy/article/cc-under-the-hood
Спасибо, стало яснее, т.е. в итоге получается такая картина: Мы запускаем в каком-то диспетчере корутину(Например, executor), вызываем suspend-функцию, доходим до блокирующего вызова, который возвращает courutine_suspend, записываем новый лэйбл, откуда нужно продолжить, и записываем в linked list сontinuation, идем наверх по стеку, оборачиваем этот linked list как таски, а диспатчер как-нибудь это все задиспатчит. Насчет последнего есть сомнения.
да, там вроде список связанных continuation. думаю для понимания таких деталей, уже проще будет капнуть исходный код, таких подробных статей я уже не видел вроде
Понял, еще раз спасибо.
Не совсем так. Continuation никуда не складывается, он остаётся на руках у того, кто должен предоставить результат операции, и оттуда дёргается resume. Например, из коллбека. А вообще в @kotlin_lang наверное эффективнее такое спрашивать
Но resume же дергает сам диспатчер, значит они у него должны быть, нет?
Нет, resume дёргает кто-то снаружи, и только там они и хранятся. Диспатчер не следит за всеми созданными continuation. Всё, что он может - обернуть их в свои, которые в resume кидают таску в этот диспатчер.
Хм, не совсем понял, например, здесь, кто будет дергать resume: suspend fun main() = coroutineScope { repeat(1000) { launch(Dispatchers.IO) { Thread.sleep(200) val threadName = Thread.currentThread().name println("Running on thread: $threadName") }
Здесь вообще засыпания нет, Thread.sleep() очевидно просто блокирует тред на время ожидания
Хм, а думал, что вернется courutine_suspend, а потом кто-нибудь сделает resume и тогда уже поток уснет, но не суть, поменять sleep на какой-нибудь вызов с засыпанием.
Ну да, так и есть. При засыпании возвращается маркер, а continuation остаётся на руках у этого кого-то. В это время диспатчер пойдёт выполнять следующую таску или спать, а этот кто-то может дёрнуть resume, когда посчитает нужным.
Да, но в этом примере никто явно не вызывает resume, поэтому и не понятно, а кто этот кто-то.
Так это деталь реализации suspend функции, которая непосредственно засыпает. Всё зависит от того, что за операция. А в прикладном коде не должно быть нигде явного resume. Тут есть пример, как это может выглядеть: https://github.com/Kotlin/KEEP/blob/master/proposals/coroutines.md#wrapping-callbacks
Почитал, т.е. resume делается в каком-то колбэке suspend-функции, а сам диспетчер просто предоставляет свою реалзиацию continutaion и при вызове resume кидает задачу в executor, например. Поэтому, я так понимаю, и нельзя блокирующие операции в корутинах делать, потому что это лишь абстракция над колбэками.
Ну не совсем, зависит от диспатчера. IO например создан специально для таких операций, там много потоков может создаваться. Но когда есть выбор, лучше, конечно, не блокировать.
Ну да, я имел в виду, что это заблокирует реальный поток, это не грин треды. В общем, спасибо.
Обсуждают сегодня