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

相关推荐
Kapaseker10 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
A0微声z2 天前
Kotlin Multiplatform (KMP) 中使用 Protobuf
kotlin
alexhilton3 天前
使用FunctionGemma进行设备端函数调用
android·kotlin·android jetpack
lhDream3 天前
Kotlin 开发者必看!JetBrains 开源 LLM 框架 Koog 快速上手指南(含示例)
kotlin
RdoZam3 天前
Android-封装基类Activity\Fragment,从0到1记录
android·kotlin
Kapaseker4 天前
研究表明,开发者对Kotlin集合的了解不到 20%
android·kotlin
糖猫猫cc4 天前
Kite:两种方式实现动态表名
java·kotlin·orm·kite
如此风景4 天前
kotlin协程学习小计
android·kotlin
Kapaseker5 天前
你搞得懂这 15 个 Android 架构问题吗
android·kotlin
zh_xuan5 天前
kotlin 高阶函数用法
开发语言·kotlin