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背压后的触发操作,启动加载。接收到的数据,在此框架中并未有实际作用。

相关推荐
胡致和16 小时前
配置变更后,弹窗为什么飞到了最左边?
kotlin
zhangphil21 小时前
Android Page 3 Flow读sql数据库媒体文件,Kotlin
android·kotlin
小书房21 小时前
Kotlin使用体验及理解1
android·开发语言·kotlin
Kapaseker1 天前
我想让同事知道我很懂 Compose 怎么办?
android·kotlin
jinanwuhuaguo1 天前
OpenClaw工程解剖——RAG、向量织构与“记忆宫殿”的索引拓扑学(第十三篇)
android·开发语言·人工智能·kotlin·拓扑学·openclaw
jinanwuhuaguo2 天前
OpenClaw协议霸权——从 MCP 标准到意图封建化的政治经济学(第十八篇)
android·人工智能·kotlin·拓扑学·openclaw
zhangphil2 天前
Android sql查媒体数据封装room Dao构造AndroidViewModel,RecyclerView宫格展示,Kotlin
android·kotlin
jinanwuhuaguo2 天前
反熵共同体——OpenClaw的宇宙热力学本体论(第十七篇)
大数据·人工智能·安全·架构·kotlin·openclaw
pengyu2 天前
【Kotlin 协程修仙录 · 筑基境 · 中阶】 | 身份证与通行证:CoroutineContext 的深度解剖
android·kotlin
夏沫琅琊2 天前
android 短信读取与导出技术
android·kotlin