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