理解kotlin limitedParallelism(1)与Actor模型

Dispatcher.kt

复制代码
package com.example.demo.process

import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.asCoroutineDispatcher
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.TimeUnit
import kotlin.concurrent.atomics.AtomicInt
import kotlin.concurrent.atomics.ExperimentalAtomicApi
import kotlin.concurrent.atomics.incrementAndFetch

object Dispatcher {
    private val logger = KotlinLogging.logger {}

    //获取当前机器的 CPU 核心数,后续所有线程池大小的计算都基于此
    private val cpuCore = Runtime.getRuntime().availableProcessors()

    //使用 AtomicInt 作为线程编号的原子计数器,保证线程命名的唯一性和线程安全。
    @OptIn(ExperimentalAtomicApi::class)
    private val newThreadId: AtomicInt = AtomicInt(0)

    @OptIn(ExperimentalAtomicApi::class)
    private fun createDispatcherThreadPool(name: String, minSize: Int, maxSize: Int): CoroutineDispatcher {
        //核心线程数(下限 ≥1)
        val corePoolSize = minSize.coerceAtLeast(1)

        //最大线程数(范围:corePoolSize ~ 128)
        val maximumPoolSize = maxSize.coerceIn(corePoolSize, 128)

        logger.info { "create dispatcher $name, corePoolSize=$corePoolSize, maximumPoolSize=$maximumPoolSize" }

        return ThreadPoolExecutor(
            corePoolSize,                       //核心线程数,源码:workerCountOf(c) < corePoolSize就会创建线程,所以就算是是:任务一个执行完了来,
            maximumPoolSize,                    // TODO 此参数其实压根不会生效,因为无界队列
            30,                   //回收非核心线程。 源码中: workQueue.poll(30, SECONDS)  ← 限时等待 30 秒, 如果线程没有任务,就会被回收(非核心线程),核心线程不会回收!!!
            TimeUnit.SECONDS,
            LinkedBlockingQueue(),  // 无界队列
        ) { runnable ->
            /*
            线程命名 :格式为 {name}-{自增ID} ,例如 Scheduler-1 、 Actor-3 ,便于 jstack 排查。
            且是守护线程
             */
            Thread(runnable, "$name-${newThreadId.incrementAndFetch()}").apply { isDaemon = true }
        }.asCoroutineDispatcher() //是 Kotlin 协程库( kotlinx.coroutines )提供的一个扩展函数,定义在 java.util.concurrent.Executor 上,用于把 Java 线程池(Executor) 桥接到 Kotlin 协程的调度器体系 中
    }

    // 轻量级调度器,固定 2 个核心线程,适合处理定时任务、延迟任务等轻量操作。
    val Scheduler = createDispatcherThreadPool("Scheduler", 2, cpuCore / 2)

    // 主力执行调度器,至少有 10 个核心线程,承载进程内大部分的业务逻辑执行
    //Dispatcher.Actor.limitedParallelism(1)的意思是:复用这个Actor调度器的底层线程池,然后再创建一个子调度器,并发度是1,这样子
    val Actor = createDispatcherThreadPool("Actor", cpuCore.coerceAtLeast(10), cpuCore * 2)
}

main.kt

复制代码
package com.example.demo

import com.example.demo.process.Dispatcher
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import org.springframework.boot.autoconfigure.SpringBootApplication
import java.util.concurrent.TimeUnit

@SpringBootApplication
class DemoApplication

fun main(args: Array<String>) {
//    runApplication<DemoApplication>(*args)

    val actorScope = CoroutineScope(CoroutineName("DemoApplication") + Dispatcher.Actor)
    val schedulerScope = CoroutineScope(Dispatcher.Scheduler)


    //------------------------------------test1-------------------------------------
    var count = 0

    actorScope.launch(CoroutineName("TIMER")) {
        for (i in 1..10000) {
            //上下文调度器本来就是多个协程可以启动
            launch {
                count++
            }
        }
    }

    TimeUnit.SECONDS.sleep(5)

    println(">>>>>count:${count}")

    //------------------------------------test2-------------------------------------

    var limitedParallelismCounter = 0
    actorScope.launch(Dispatcher.Actor.limitedParallelism(1) + CoroutineName("limitedParallelism")) {
        for (i in 1..10000) {
            // 默认使用父协程上下文(也就是并发度也是1的调度器,起多个协程业没用)
            launch {
                limitedParallelismCounter++
            }
        }
    }

    TimeUnit.SECONDS.sleep(5)

    println("-----limitedParallelismCounter:${limitedParallelismCounter}")

    //------------------------------------test3-------------------------------------

    var limitedParallelismAnotherCounter = 0
    actorScope.launch(Dispatcher.Actor.limitedParallelism(1) + CoroutineName("limitedParallelism")) {
        for (i in 1..10000) {
            // 换了上下文(也就是又变成多线程了)
            schedulerScope.launch {
                limitedParallelismAnotherCounter++
            }
        }
    }

    TimeUnit.SECONDS.sleep(5)

    println("=====limitedParallelismAnotherCounter:${limitedParallelismAnotherCounter}")


    //------------------------------------test4-------------------------------------

    var limitedParallelismSameCounter = 0
    actorScope.launch(Dispatcher.Actor.limitedParallelism(1) + CoroutineName("limitedParallelism")) {
        for (i in 1..10000) {
            // 其实协程作用阈还是变了
            actorScope.launch {
                limitedParallelismSameCounter++
            }
        }
    }

    TimeUnit.SECONDS.sleep(5)

    println("*****limitedParallelismAnotherCounter:${limitedParallelismSameCounter}")
}

/*
15:22:27.868 [main] INFO com.example.demo.process.Dispatcher -- create dispatcher Scheduler, corePoolSize=2, maximumPoolSize=8
15:22:27.873 [main] INFO com.example.demo.process.Dispatcher -- create dispatcher Actor, corePoolSize=16, maximumPoolSize=32
>>>>>count:9974
-----limitedParallelismCounter:10000
=====limitedParallelismAnotherCounter:9969
*****limitedParallelismAnotherCounter:9962
 */

总结:

复制代码
actorScope.launch(Dispatcher.Actor.limitedParallelism(1) + CoroutineName("limitedParallelism")) 

通过传入协程上下文,限制该协程下最多只有一个协程在运行,这样子实现Actor模型

相关推荐
撩得Android一次心动几秒前
Kotlin 语言【知识点整理2】
android·开发语言·kotlin
不会就选b5 分钟前
Linux之线程池(一)
java·开发语言
福大大架构师每日一题8 分钟前
webrtc-rs/webrtc v0.20.3更新:Android 网络切换后 ICE Restart 卡死约 10 秒的问题终于解决
android·网络·webrtc
牛油果子哥q19 分钟前
C++序列式容器深度精讲:vector/list/deque底层实现、扩容原理、迭代器失效、性能对比、工程选型避坑
开发语言·c++·list
mmsx25 分钟前
基于 Android 的本地商城订单 App:SQLite + 多角色架构的技术实践(有源码和文档)
android·架构·sqlite
渡我白衣32 分钟前
Acceptor模块的设计与实现
java·linux·服务器·开发语言·网络·c++·人工智能
沫璃染墨1 小时前
《从零入门Linux系统篇(二十八):文件篇·一——Linux为什么“万物皆文件”:从C语言文件流到系统调用》
linux·运维·服务器·开发语言·c++·文件
luteres2 小时前
Mybatis+SSM整合笔记
java·开发语言·mybatis
郝学胜-神的一滴2 小时前
《C++11 工程级应用01:告别冗长类型,开启简洁高效编码新时代》深度解读
开发语言·c++·算法·软件开发·系统设计
其实防守也摸鱼3 小时前
推荐一个自动化教育SRC漏洞挖掘系统--AutoHunter
运维·开发语言·人工智能·学习·安全·web安全·自动化