Kotlin协程flow缓冲buffer任务流,批次任务中选取优先级最高任务最先运行(四)

Kotlin协程flow缓冲buffer任务流,批次任务中选取优先级最高任务最先运行(四)

https://blog.csdn.net/zhangphil/article/details/156990010 基础上改进。

Kotlin 复制代码
import kotlinx.coroutines.cancel
import kotlinx.coroutines.newFixedThreadPoolContext
import kotlin.coroutines.cancellation.CancellationException

open class Loader {
    companion object {
        val THREAD_POOL = newFixedThreadPoolContext(nThreads = 4, name = "my-thread")
    }

    constructor() {

    }

    open fun load(taskInfo: TaskEntity) {
    }

    fun cancel() {
        THREAD_POOL.cancel(CancellationException(""))
    }
}
Kotlin 复制代码
class MyLoader : Loader() {
    companion object {
        const val TAG = "MyLoader"
    }

    override fun load(taskEntity: TaskEntity) {
        println("$TAG 开始加载 $taskEntity ${Thread.currentThread().name}")
        //假设耗时操作
        Thread.sleep(500)
        //println("$TAG load end $info ${Thread.currentThread().name}")
    }
}
Kotlin 复制代码
import java.util.UUID

class TaskEntity {
    var id = 0
    var priority = 0

    private val taskId = UUID.randomUUID()

    constructor(id: Int, priority: Int = (Math.random() * 999).toInt()) {
        this.id = id
        this.priority = priority
    }

    override fun equals(other: Any?): Boolean {
        return taskId == (other as TaskEntity).taskId
    }

    override fun toString(): String {
        return "TaskEntity(id=$id, priority=$priority)"
    }
}
Kotlin 复制代码
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.buffer
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.receiveAsFlow
import java.util.concurrent.PriorityBlockingQueue


class TaskQueue {
    companion object {
        private const val TAG = "TaskQueue"
    }

    private val mChannel = Channel<TaskEntity>()

    private val bufferCapacity = 10
    private val initialCapacity = 50

    private val mPriorityBlockingQueue = PriorityBlockingQueue(
        initialCapacity,
        Comparator<TaskEntity> { o1, o2 -> o2.priority - o1.priority })

    private var mLoader: Loader? = null

    constructor(loader: Loader) {
        mLoader = loader
    }

    fun start() {
        //接收任务
        CoroutineScope(Loader.THREAD_POOL).async {
            println("$TAG Channel start... ${Thread.currentThread().name}")

            mChannel.receiveAsFlow()
                .onEach { it ->  //生产者
                    //println("$TAG onEach-$it ${Thread.currentThread().name}")
                }.buffer(bufferCapacity)
                .collect { it -> //消费者
                    //collect, 这里相当于通过缓冲后匀速发射过来的触发器(trigger)。
                    //收集到的值在此并不重要,这里,只是把它作为触发信号。
                    //println("$TAG collect-$it ${Thread.currentThread().name}")
                    trigger()
                }
        }
    }

    private fun trigger() {
        val taskEntity = mPriorityBlockingQueue.poll()
        //println("$TAG 最大优先级任务:$taskEntity ${Thread.currentThread().name}")

        taskEntity?.let {
            CoroutineScope(Loader.THREAD_POOL).async {
                mLoader?.load(it)
            }
        }
    }

    fun enqueue(taskInfo: TaskEntity) {
        CoroutineScope(Loader.THREAD_POOL).async {
            println("$TAG send $taskInfo  ${Thread.currentThread().name}")
            mPriorityBlockingQueue.add(taskInfo)
            mChannel.send(taskInfo)
        }
    }

    fun destroy() {
        mChannel.cancel()
        mChannel.close()

        mLoader?.cancel()
    }
}

测试的main:

Kotlin 复制代码
const val TAG = "main"

fun main() {
    val loader = MyLoader()
    val taskQueue = TaskQueue(loader)
    taskQueue.start()

    //源源不断的密集发送加载任务。
    repeat(20) { it ->
        val taskEntity = TaskEntity(it)
        taskQueue.enqueue(taskEntity)
    }
    println("$TAG enqueue 全部完成")

    Thread.sleep(20000)
    println("$TAG sleep结束")

    taskQueue.destroy()
}

运行输出:

TaskQueue Channel start... my-thread-1

main enqueue 全部完成

TaskQueue send TaskEntity(id=4, priority=245) my-thread-5

TaskQueue send TaskEntity(id=0, priority=191) my-thread-2

TaskQueue send TaskEntity(id=3, priority=317) my-thread-4

TaskQueue send TaskEntity(id=2, priority=164) my-thread-3

TaskQueue send TaskEntity(id=1, priority=461) my-thread-1

TaskQueue send TaskEntity(id=7, priority=128) my-thread-3

TaskQueue send TaskEntity(id=8, priority=334) my-thread-1

TaskQueue send TaskEntity(id=9, priority=486) my-thread-1

TaskQueue send TaskEntity(id=5, priority=643) my-thread-4

TaskQueue send TaskEntity(id=10, priority=723) my-thread-3

TaskQueue send TaskEntity(id=12, priority=901) my-thread-4

TaskQueue send TaskEntity(id=6, priority=540) my-thread-2

TaskQueue send TaskEntity(id=14, priority=554) my-thread-4

TaskQueue send TaskEntity(id=11, priority=122) my-thread-1

TaskQueue send TaskEntity(id=13, priority=730) my-thread-3

TaskQueue send TaskEntity(id=16, priority=773) my-thread-3

TaskQueue send TaskEntity(id=15, priority=741) my-thread-1

TaskQueue send TaskEntity(id=17, priority=923) my-thread-3

TaskQueue send TaskEntity(id=18, priority=231) my-thread-2

TaskQueue send TaskEntity(id=19, priority=503) my-thread-3

MyLoader 开始加载 TaskEntity(id=17, priority=923) my-thread-3

MyLoader 开始加载 TaskEntity(id=12, priority=901) my-thread-2

MyLoader 开始加载 TaskEntity(id=16, priority=773) my-thread-5

MyLoader 开始加载 TaskEntity(id=15, priority=741) my-thread-1

MyLoader 开始加载 TaskEntity(id=13, priority=730) my-thread-4

MyLoader 开始加载 TaskEntity(id=10, priority=723) my-thread-2

MyLoader 开始加载 TaskEntity(id=5, priority=643) my-thread-3

MyLoader 开始加载 TaskEntity(id=14, priority=554) my-thread-1

MyLoader 开始加载 TaskEntity(id=6, priority=540) my-thread-5

MyLoader 开始加载 TaskEntity(id=19, priority=503) my-thread-4

MyLoader 开始加载 TaskEntity(id=9, priority=486) my-thread-2

MyLoader 开始加载 TaskEntity(id=3, priority=317) my-thread-3

MyLoader 开始加载 TaskEntity(id=1, priority=461) my-thread-5

MyLoader 开始加载 TaskEntity(id=8, priority=334) my-thread-1

MyLoader 开始加载 TaskEntity(id=4, priority=245) my-thread-4

MyLoader 开始加载 TaskEntity(id=0, priority=191) my-thread-3

MyLoader 开始加载 TaskEntity(id=18, priority=231) my-thread-2

MyLoader 开始加载 TaskEntity(id=2, priority=164) my-thread-1

MyLoader 开始加载 TaskEntity(id=7, priority=128) my-thread-5

MyLoader 开始加载 TaskEntity(id=11, priority=122) my-thread-4

main sleep结束

Process finished with exit code 0


1、观察运行输出可以看到,程序总体上按照预期的优先级从高到低执行任务。但是在个别几行,优先级低的反而出现在优先级高的前面得到执行(如632)。这不是什么错误,是因为框架在加载过程中,共享有限的5线程THREAD_POOL空间。当每一条MyLoader里面执行耗时任务时,那么MyLoader会长时间占用一个线程,线程池数量有限,在某些时间段内,优先级高的虽得到调度,但都处于等待之前任务完成后释放线程空间。如果MyLoader里面几乎不存在较长耗时任务(如10ms以内),运行任务的优先级完美契合从高到低的顺序。

解决这个问题其实相对简单:(1)扩大THREAD_POOL的数量,目的是把任务充分均匀得到调度,并发执行,不必因为线程池数量有限而产生竞争和等到调度。(2)把trigger()触发的加载任务不要再开启独立的协程(CoroutineScope(Loader.THREAD_POOL).async)运行,而是直接运行,但这意味着,所有加载任务将被排队在一个collect线程里面执行,效率很成问题。

(3)尽可能降低MyLoader的耗时。目的是不要长时间占用线程池中资源。

2、flow里面的collect只是一个触发器,接收来自onEach背压后的触发操作,启动加载。接收到的数据,在此框架中并未有实际作用。

相关推荐
低调小一21 小时前
Kotlin 2025–2026 客户端开发路线:语言升级 × 跨端落地 × AI Agent 入门
开发语言·人工智能·kotlin
恋猫de小郭1 天前
Flutter 又迎大坑修改?iOS 26 键盘变化可能带来大量底层改动
android·flutter·ios·kotlin
zhangphil1 天前
Kotlin IDEA配置kotlin-maven-plugin、kotlin-stdlib、kotlinx-coroutines-core最新版本
kotlin·maven
冬奇Lab1 天前
【Kotlin系列09】委托机制与属性委托实战:组合优于继承的最佳实践
android·开发语言·kotlin
低调小一2 天前
深度复盘:KMP 在字节跳动的工程化落地实践
android·kotlin
zh_xuan2 天前
kotlin 密封类
开发语言·kotlin
移幻漂流2 天前
Kotlin 如何解决 Java 的核心痛点:现代语言特性的深度剖析
java·python·kotlin
zhangphil2 天前
Kotlin实现Glide/Coil图/视频加载框架(二)
android·kotlin
Rubin智造社2 天前
见路不走:从《天幕红尘》读懂2026年的创新密码
android·开发语言·kotlin