SendChannel 与 ReceiveChannel 通信机制

一、核心接口分工

SendChannel :作为通道的发送端,仅暴露数据写入能力。其核心方法为挂起函数 send(element: E),用于向通道提交数据。当缓冲区已满时,当前协程会自动挂起而非阻塞线程,同时支持 trySend 非阻塞尝试发送以及 close() 关闭通道等操作。

ReceiveChannel :作为通道的接收端,仅暴露数据读取能力。其核心方法为挂起函数 receive(): E,用于从通道取出数据。当通道为空时,当前协程会自动挂起等待新数据到达,同时支持 tryReceive 非阻塞尝试接收,亦可直接使用 for-in 循环遍历通道内所有元素直至通道关闭。

二、使用示例

kotlin 复制代码
fun main() = runBlocking {
    // 创建默认无缓冲通道,同时拥有发送端和接收端
    val channel = Channel<Int>()
    // 取出 SendChannel 侧,仅可用于发送数据
    val sender: SendChannel<Int> = channel
    // 取出 ReceiveChannel 侧,仅可用于接收数据
    val receiver: ReceiveChannel<Int> = channel
​
    // 生产者协程:通过 SendChannel 发送数据
    launch {
        repeat(5) {
            sender.send(it)
            println("Sent: $it")
        }
        sender.close() // 发送完成后关闭通道
    }
​
    // 消费者协程:通过 ReceiveChannel 接收数据
    launch {
        // for 循环自动遍历,通道关闭后自动结束循环
        for (element in receiver) {
            println("Received: $element")
        }
        println("通信结束")
    }
​
    delay(1000)
}

三、编译器生成的迭代逻辑

编译器实际生成的逻辑等价于:

scss 复制代码
val iterator = receiver.iterator() // 获取迭代器
while (iterator.hasNext()) {       // 判断是否有下一个元素
    val element = iterator.next()  // 获取下一个元素
    println(element)
}

四、Channel 实现类

kotlin 复制代码
public fun <E> Channel(
    capacity: Int = RENDEZVOUS,
    onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND,
    onUndeliveredElement: ((E) -> Unit)? = null
): Channel<E> {
    // 这里的 when 判断用于选择具体的实现类
    return when (capacity) {
        RENDEZVOUS -> RendezvousChannel(onUndeliveredElement) // 无缓冲,容量为 0
        CONFLATED -> ConflatedChannel(onUndeliveredElement)   // Conflated,容量为 -1
        UNLIMITED -> LinkedListChannel(onUndeliveredElement)  // 无限缓冲,使用链表
        else -> ArrayChannel(capacity, onBufferOverflow, onUndeliveredElement) // 有界缓冲,使用数组
    }
}

上述实现类均继承自共同的基类 AbstractChannel。

kotlin 复制代码
// AbstractChannel.kt 简化源码逻辑
abstract class AbstractChannel<E>(
    private val onUndeliveredElement: ((E) -> Unit)?
) : Channel<E>, SendChannel<E>, ReceiveChannel<E> {
​
    // 1. 提供迭代器入口
    public final override fun iterator(): ChannelIterator<E> = Itr(this)
​
    // ... 其他 send/receive 逻辑
}

五、迭代器实现原理

kotlin 复制代码
private class Itr<E>(@JvmField val channel: AbstractChannel<E>) : ChannelIterator<E> {
    var result: Any? = POLL_FAILED // E | POLL_FAILED | Closed
​
    override suspend fun hasNext(): Boolean {
        // check for repeated hasNext
        if (result !== POLL_FAILED) return hasNextResult(result)
        // fast path -- try poll non-blocking
        result = channel.pollInternal()
        if (result !== POLL_FAILED) return hasNextResult(result)
        // slow-path does suspend
        return hasNextSuspend()
    }
​
    private fun hasNextResult(result: Any?): Boolean {
        if (result is Closed<*>) {
            if (result.closeCause != null) throw recoverStackTrace(result.receiveException)
            return false
        }
        return true
    }
​
    private suspend fun hasNextSuspend(): Boolean = suspendCancellableCoroutineReusable sc@ { cont ->
        val receive = ReceiveHasNext(this, cont)
        while (true) {
            if (channel.enqueueReceive(receive)) {
                channel.removeReceiveOnCancel(cont, receive)
                return@sc
            }
            // hm... something is not right. try to poll
            val result = channel.pollInternal()
            this.result = result
            if (result is Closed<*>) {
                if (result.closeCause == null)
                    cont.resume(false)
                else
                    cont.resumeWithException(result.receiveException)
                return@sc
            }
            if (result !== POLL_FAILED) {
                @Suppress("UNCHECKED_CAST")
                cont.resume(true, channel.onUndeliveredElement?.bindCancellationFun(result as E, cont.context))
                return@sc
            }
        }
    }
​
    @Suppress("UNCHECKED_CAST")
    override fun next(): E {
        val result = this.result
        if (result is Closed<*>) throw recoverStackTrace(result.receiveException)
        if (result !== POLL_FAILED) {
            this.result = POLL_FAILED
            return result as E
        }
​
        throw IllegalStateException("'hasNext' should be called prior to 'next' invocation")
    }
}

pollInternal 内部,this.result 被赋值为以下结果:

ini 复制代码
val result = channel.pollInternal()
this.result = result

六、pollInternal 函数

kotlin 复制代码
protected open fun pollInternal(): Any? {
    while (true) {
        val send = takeFirstSendOrPeekClosed() ?: return POLL_FAILED
        val token = send.tryResumeSend(null)
        if (token != null) {
            assert { token === RESUME_TOKEN }
            send.completeResumeSend()
            return send.pollResult
        }
        // too late, already cancelled, but we removed it from the queue and need to notify on undelivered element
        send.undeliveredElement()
    }
}
​
protected fun takeFirstSendOrPeekClosed(): Send? =
    queue.removeFirstIfIsInstanceOfOrPeekIf<Send> { it is Closed<*> }

七、内部队列机制

Channel 的设计灵感来源于 Java 中的 BlockingQueue,但其专为非阻塞挂起协程而设计。

生产者(SendChannel) :调用 send(element) 时,实际上是将数据元素封装成一个节点,放入这个内部队列的尾部。如果队列已满(对于有界 Channel),发送协程会被挂起,直到有空间可用。

消费者(ReceiveChannel) :调用 receive() 时,实际上是从这个内部队列的头部取出数据元素。如果队列为空,接收协程会被挂起,直到有新数据入队或通道关闭。

相关推荐
数据治理自习室2 小时前
AI 应用评测体系(GraphRAG)
android·大数据·人工智能·kotlin
爱笑鱼3 小时前
Android 系统启动机制(五):system_server 是 init 启动的,还是 Zygote fork 出来的?
android
智购科技无人售货机工厂3 小时前
2026自动售货机防拆机物理安全设计:从安全螺丝到结构互锁的工程实践~YH
android·网络·驱动开发·python·单片机·安全·云原生
开开心心就好4 小时前
电子教鞭工具支持画框写字插图片功能齐全
android·开发语言·前端·javascript·人工智能·pdf·html
Dovis(誓平步青云)4 小时前
拍视频前先把镜头想清楚:做一个分镜取景辅助器
android·java·服务器·javascript·人工智能
峥嵘life5 小时前
Android16 系统 APEX 模块说明
android·大数据·开发语言
2601_962065496 小时前
PHP For 循环
android·java·php
码农coding6 小时前
android12 WindowManagerService窗口的添加过程
android
小小测试开发6 小时前
RAG评测指标实战:忠实度、上下文精确率/召回率从原理到CI落地
android·人工智能·spring boot·ci/cd