Me gustaría suspender una rutina de Kotlin hasta que se llame a un método desde el exterior, al igual que los antiguos métodos Java object.wait() y object.notify(). ¿Cómo puedo hacer eso?
Aquí: Implementar correctamente esperar y notificar en Kotlin es una respuesta sobre cómo implementar esto con hilos de Kotlin (bloqueo). Y aquí: Suspender la rutina hasta que la condición sea verdadera es una respuesta sobre cómo hacer esto con CompleteableDeferreds, pero no quiero tener que crear una nueva instancia de CompleteableDeferred cada vez.
Estoy haciendo esto actualmente:
var nextIndex = 0 fun handleNext(): Boolean { if (nextIndex < apps.size) { //Do the actual work on apps[nextIndex] nextIndex++ } //only execute again if nextIndex is a valid index return nextIndex < apps.size } handleNext() // The returned function will be called multiple times, which I would like to replace with something like notify() return ::handleNextLos canales se pueden usar para esto (aunque son más generales):
Cuando la capacidad es 0, crea RendezvousChannel. Este canal no tiene ningún búfer en absoluto. Un elemento se transfiere del remitente al receptor solo cuando las invocaciones de enviar y recibir se encuentran a tiempo (rendezvous), por lo tanto, el envío se suspende hasta que otra corrutina invoca a recibir y la recepción se suspende hasta que otra corrutina invoca a enviar.
Así que crea
val channel = Channel<Unit>(0) Y use channel.receive() para object.wait() , y channel.offer(Unit) para object.notify() (o send si desea esperar hasta que la otra rutina receive s).
Para notifyAll a Todos, puede usar BroadcastChannel en su lugar.
Por supuesto, puede encapsularlo fácilmente:
inline class Waiter(private val channel: Channel<Unit> = Channel<Unit>(0)) { suspend fun doWait() { channel.receive() } fun doNotify() { channel.offer(Unit) } }Es posible usar la función básica suspendCoroutine{..} para eso, por ejemplo
class SuspendWait() { private lateinit var myCont: Continuation<Unit> suspend fun sleepAndWait() = suspendCoroutine<Unit>{ cont -> myCont = cont } fun resume() { val cont = myCont myCont = null cont.resume(Unit) } } Está claro, el código tiene problemas, por ejemplo, el campo myCont no está sincronizado, se espera que se sleepAndWait antes de la resume y así sucesivamente, espero que la idea esté clara ahora.
Hay otra solución con la clase Mutex de la biblioteca kotlinx.coroutines.
class SuspendWait2 { private val mutex = Mutex(locaked = true) suspend fun sleepAndWait() = mutex.withLock{} fun resume() { mutex.unlock() } }Sugiero usar un CompletableJob para eso.
Mi caso de uso:
suspend fun onLoad() { var job1: CompletableJob? = Job() var job2: CompletableJob? = Job() lifecycleScope.launch { someList.collect { doSomething(it) job1?.complete() } } lifecycleScope.launch { otherList.collect { doSomethingElse(it) job2?.complete() } } joinAll(job1!!, job2!!) // suspends until both jobs are done job1 = null job2 = null // Do something one time }