Kotlin+协程+FLow+Channel,实现生产消费者模式3种案例

生成者消费模式,在设计中也是一种频繁的应用,掌握它便能轻松应对各种场景

一、前言

生产消费者模式在Andoird 里面应用还相当广泛,它带来的好处大致可以分为:

  1. 解耦:能彻底解耦生产模块和消费模块,生产者和消费者之间不直接进行交互,而是通过一个缓冲区(如队列或缓冲区)来进行交互。这种方式减少了生产者和消费者之间的直接依赖关系,使得系统的各个部分更加独立,易于维护和扩展‌
  2. 同时,它也能平衡生产消费之间的速度。在多线程环境中,生产者和消费者的处理速度往往是不一致的。生产者-消费者模式通过缓冲区来平衡这种速度差异,生产者只需将数据放入缓冲区,而消费者从缓冲区取出数据处理。这样,生产者不需要等待消费者处理完数据再继续生产,反之亦然,从而提高了系统的整体效率‌
  3. 提高系统稳定性和效率‌:通过缓冲区的使用,系统可以在生产者速度过快或消费者速度过慢时进行调节,避免了直接的生产者-消费者间的等待,减少了系统负载,提高了程序的稳定性和效率‌
  4. 应用广泛 ‌:生产者-消费者模式在许多系统中都有应用:
    Android系统中的Handler,Looper, message,MessageQueue‌ 它们的运行模式就是一种生成消费者模式。
    同样:在Android直播应用中 :我们把每一帧的数据编码好,准备成传输数据包: RTMPPackage 加入到缓冲队列里面,这个过程就是数据生成阶段,另一边我们不停地从缓冲队列里面取出 传输数据包: RTMPPackage,推送给服务端,这个过程就是消费阶段。
  • 这里需要注意的,生产消费者模式并不是23种设计模式中的。

  • 本文主要介绍:Kotlin+协程+FLow+Channel,实现生产消费者模式几种案例

二、案例一,传统式写法:

  • 生产者向缓冲区产生数据,当缓冲区超过设置最大数时候,让其等待,等待消费者处理完了,再加入进去。
  • 当消费者发现缓冲区为空时候,等待,知道有可以消费的数据时候为止。
  • 关键点:使用while循环检查条件避免虚假唤醒,notifyAll确保唤醒正确线程
kotlin 复制代码
val buffer = LinkedList<Int>()
val MAX_SIZE = 5
val lock = Object()

// 生产者
fun produce(item: Int) {
    synchronized(lock) {
        while (buffer.size >= MAX_SIZE) {
            lock.wait()  // 缓冲区满时等待
        }
        buffer.add(item)
        lock.notifyAll() // 必须使用notifyAll避免死锁
    }
}

// 消费者
fun consume(): Int {
    synchronized(lock) {
        while (buffer.isEmpty()) {
            lock.wait()  // 缓冲区空时等待
        }
        val item = buffer.removeFirst()
        lock.notifyAll()
        return item
    }
}

// 测试代码
fun main() {
    repeat(3) { i ->
        Thread { produce(i) }.start() // 启动3个生产者
    }
    repeat(2) { 
        Thread { println(consume()) }.start() // 启动2个消费者
    }

从上面我们可以看出,最经典的,是完全自由的,可完全自定义的,涉及到缓存,最大值,锁相关知识,以及如果 异步 也需要自己来实现管理处理。这样写起来需要基本功深才行。

三、案例二、协程+Channel方式实现

下面示例:

我们创建了容量为5的缓冲通道,

生产者每100ms发送数据,

消费者每200ms处理数据,

自动实现流量控制。

当缓冲区满时send挂起,

空时receive挂起

同时,由于我们协程中,我们可以通过直接通过协程中的 Dispatchers来直接切换线程,让其异步中实现生产和消费。

scss 复制代码
fun main() = runBlocking {
    // 创建容量为5的缓冲通道
    val channel = Channel<Int>(capacity = 5)
    
    // 生产者协程
    val producer = launch {
        repeat(10) { i ->
            delay(100) // 模拟生产耗时
            channel.send(i)
            println("生产: $i (缓冲区剩余: ${channel.capacity - channel.size})")
        }
        channel.close() // 生产完成后关闭通道
    }

    // 消费者协程
    val consumer = launch {
        channel.consumeEach { item ->
            delay(200) // 模拟消费耗时
            println("消费: $item")
        }
    }

    // 等待生产和消费完成
    joinAll(producer, consumer)
}

四、案例三、协程结合Flow方式实现

如下面代码所示:

我们通过flow构建生产者流,buffer控制背压,onEach处理消费逻辑,flowOn实现线程切换 涉及到的核心点:

  1. 背压策略选择
    buffer():缓冲指定数量元素,默认溢出策略为挂起生产者
    conflate():仅保留最新元素,适合实时状态更新场景
    collectLatest():新元素到达时取消当前消费任务

  2. 线程调度控制
    flowOn(Dispatchers.IO):指定上下游执行线程池

    生产者与消费者默认共享协程上下文,需显式分离避免阻塞

  3. 异常处理增强

    可通过Flow的.catch统一处理生产阶段和消费阶段的异常。

scss 复制代码
fun producer(): Flow<Int> = flow {
    repeat(10) { i ->
        delay(100) // 模拟生产耗时
        emit(i)
        println("生产: $i")
    }
}.catch{
    e -> println("生产异常: $e")
}
.flowOn(Dispatchers.IO) // 指定生产者协程上下文

fun main() = runBlocking {
    producer()
        .buffer(5) // 设置缓冲区容量
        .onEach { item ->
            delay(200) // 模拟消费耗时
            println("消费: $item")
        }.catch{
            e -> println("生产异常: $e")
        }.flowOn(Dispatchers.IO) // 指定消费者协程上下文
        .collect()
}

从上面我们也可以看出:

Flow与Channel方案对比

特性 Flow方案 Channel方案
数据生成方式 冷流(按需生产) 热流(独立于消费)
背压处理 通过操作符(buffer/conflate)控制 依赖通道容量设置
异常处理 统一处理 需要手动分别处理
适用场景 纯数据流处理 需要双向通信的场景

五、总结

本文重点介绍了,传统生产消费者模式和 Kotlin+协程+FLow+Channel实现消费者模式

  1. 传统的完全自定义,涉及到相关内容较多,需要基础扎实
  2. 基于协程和Flow或者协程+Channel来实现,可以很简单的背压处理,切换线程处理。使用起来更加方便。

感谢阅读:

欢迎用你发财的小手 关注,点赞、收藏

这里你会学到不一样的东西

相关推荐
AI视觉网奇28 分钟前
android studio 断点无效
android·ide·android studio
jiaxi的天空31 分钟前
android studio gradle 访问不了
android·ide·android studio
程序员在线炒粉8元1份顺丰包邮送可乐44 分钟前
Docker 部署生产环境可用的 MySQL 主从架构
mysql·docker·架构
No Silver Bullet1 小时前
android组包时会把从maven私服获取的包下载到本地吗
android
catchadmin2 小时前
PHP serialize 序列化完全指南
android·开发语言·php
Angelyb3 小时前
微服务保护和分布式事务
java·微服务·架构
tangweiguo030519873 小时前
Kable使用指南:Android BLE开发的现代化解决方案
android·kotlin
失散134 小时前
分布式专题——10.1 ShardingSphere介绍
java·分布式·架构·shardingsphere·分库分表
天天爱吃肉82185 小时前
【比亚迪璇玑架构深度解析:重新定义智能电动汽车的“整车智能”】
数据库·人工智能·嵌入式硬件·架构·汽车
00后程序员张5 小时前
iOS App 混淆与资源保护:iOS配置文件加密、ipa文件安全、代码与多媒体资源防护全流程指南
android·安全·ios·小程序·uni-app·cocoa·iphone