如何暂停Kotlin协程直到通知

时间:2019-03-29 16:26:36

标签: kotlin kotlin-coroutines

我想暂停一个kotlin协程,直到从外部调用一个方法为止,就像旧的Java object.wait()和object.notify()方法一样。我该怎么办?

此处:Correctly implementing wait and notify in Kotlin是如何使用Kotlin线程(阻塞)实现此目的的答案。在这里:Suspend coroutine until condition is true是如何使用CompleteableDeferreds做到这一点的答案,但我不想每次都必须创建一个新的CompleteableDeferred实例。

我目前正在这样做:

    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 ::handleNext

发件人:https://gitlab.com/SuperFreezZ/SuperFreezZ/blob/master/src/superfreeze/tool/android/backend/Freezer.kt#L69

3 个答案:

答案 0 :(得分:4)

Channels可以用于此(尽管它们更通用):

  

当容量为0时,它将创建RendezvousChannel。该通道根本没有任何缓冲区。仅当发送和接收调用在时间上符合(交会)时,元素才会从发送方转移到接收方,因此send暂停直到另一个协程调用receive,接收暂停直到另一个coroutine调用send。

因此创建

val channel = Channel<Unit>(0)

对于object.wait()使用channel.receive(),对于object.notify()使用channel.offer(Unit)(如果要等到其他协程send,则使用receive s。

对于notifyAll,您可以改用BroadcastChannel

您当然可以轻松地封装它:

inline class Waiter(private val channel: Channel<Unit> = Channel<Unit>(0)) {

    suspend fun doWait() { channel.receive() }
    fun doNotify() { channel.offer(Unit) }
}

答案 1 :(得分:0)

为此,可以使用基本的suspendCoroutine{..}函数,例如

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)
  }
}

很明显,代码存在问题,例如myCont字段未同步,预计在sleepAndWait之前调用resume,依此类推,希望现在能弄清楚这个想法。

kotlinx.coroutines库中的Mutex类还有另一种解决方案。

class SuspendWait2 {
  private val mutex = Mutex(locaked = true)
  suspend fun sleepAndWait() = mutex.withLock{}
  fun resume() {
    mutex.unlock()
  }
}

答案 2 :(得分:0)

我建议为此使用CompletableJob

我的用例:

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
}