kotlin协程 Channel

260 阅读2分钟

前言

Kotlin协程引入了Channel和Flow,处理数据流,实现生成者-消费者模式,构建响应式应用。处理数据流和异步操作。

  • Channel是一种热流
  • Flow是一种冷流

Channel异步数据通信

Channel是用于协程之间通信的数据结构。内部基于协程调度器和锁。用队列存储发送数据,锁实现线程安全的数据访问。如果队列已满,发送协程将被挂起,直到有空间可用。接收协程会从队列中取出数据,如果队列为空,接收协程会被挂起,直到有数据可用。

有界Channel限制发送到Channel的数据量,无界Channel不做限制。

  • capacity:Channel的容量,调用send发送数据,当receive消费慢的时候,会占用这个容量,当这个容量满后会触发丢弃策略(onBufferOverflow)。
  • onBufferOverflow:当capacity溢出后触发
  • onUndeliveredElement:当onBufferOverFlow为DROP_OLDEST或DROP_LATEST时,丢弃数据,关闭后继续发送消息,也会回调该对象

onBufferOverflow的取值:

  1. SUSPEND:将send方法挂起,直到capacity容量可以用为止(生产者剩余量小于capacity)
  2. DROP_OLDEST:丢弃最旧的数据
  3. DROP_LATEST:丢弃最新的数据
fun main(): Unit = runBlocking {
    println("runBlocking")
    val channel = Channel<Int>()
    launch {
        for (i in 1..5) {
            delay(1000)
            println(
                "send i:$i threadName:${Thread.currentThread().name} " +
                        "threadId:${Thread.currentThread().id}"
            )
            channel.send(i)
        }
        // 这里注意在需要的时候关闭,不然一直处于未释放(当前线程一直在等待新消息的发送)
        channel.close()
    }
    launch {
        for (value in channel) {
            println(
                "value:$value threadName:${Thread.currentThread().name} " +
                        "threadId:${Thread.currentThread().id}"
            )
        }
    }
}
输出结果:
send i:1 threadName:Test worker @coroutine#2 threadId:1
value:1 threadName:Test worker @coroutine#3 threadId:1
send i:2 threadName:Test worker @coroutine#2 threadId:1
value:2 threadName:Test worker @coroutine#3 threadId:1
send i:3 threadName:Test worker @coroutine#2 threadId:1
value:3 threadName:Test worker @coroutine#3 threadId:1
send i:4 threadName:Test worker @coroutine#2 threadId:1
value:4 threadName:Test worker @coroutine#3 threadId:1
send i:5 threadName:Test worker @coroutine#2 threadId:1
value:5 threadName:Test worker @coroutine#3 threadId:1

Channel,一个协程发送数据,一个协程接收数据,有助于协程之间异步通信。可以看出同threadId=1,但是不同的threadName。在同一个线程中实现了两个协程之间的发/收数据。

fun main() {
    runBlocking(Dispatchers.Default) {
        // 每次send只能有一个对应receive方法能接收到这个数据
        val myChannel = Channel<Int>()
        launch {
            repeat(3) {
                delay(100)
                myChannel.send(it)
                println("send $it")
            }
            myChannel.close()
            println("send finish")
        }
        launch {
            for (e in myChannel) {
                delay(200)
                println("receive1:$e")
            }
            println("Receive1 finish")
        }
        launch {
            for (e in myChannel) {
                delay(200)
                println("receive2:$e")
            }
            println("Receive2 finish")
        }
    }
}
输出:
send 0
send 1
receive1:0
send 2
send finish
receive2:1
Receive2 finish
receive1:2
Receive1 finish

协程1和2交替获取数据。一个接收者收到数据,那另一个就不会收到相同的数据。

fun main() {
    runBlocking(Dispatchers.Default) {
        // capacity容量,没有receive方法接收数据的时候会占用这个容量 
        // onBufferOverflow 当capacity占满后,会触发丢弃策略。DROP_OLDEST(丢弃最久的数据)
        val myChannel = Channel<Int>(capacity = 2, onBufferOverflow = BufferOverflow.DROP_OLDEST) {
            println("Send Drop: $it")
        }
        launch {
            repeat(4) {
                delay(50)
                myChannel.send(it)
                println("Send: $it")
            }
            myChannel.close()
            println("Send Finish")
        }

        launch {
            for (e in myChannel) {
                delay(300)
                println("Receive1: $e")
            }
            println("Receive1 Finish")
        }
    }
}

发送比接收速度快,Channel容量为2, 触发BufferOverFlow逻辑,超过容量后丢弃最旧的一条数据(DROP_OLDEST)