理解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模型

相关推荐
骑着蜗牛撵大象32716 小时前
让 Codex 值夜班:把告警排查与巡检接成可回滚的自动化链路
android·java·运维·ai·自动化·codex·智能体
2401_8858850416 小时前
芬兰语音通知接口怎样对接?语音提醒接入流程
android·前端·python·php·语音识别
大风起兮云飞扬丶16 小时前
Java——枚举
java·开发语言
小灰灰搞电子16 小时前
Rust+Slint 实现高级动态消息提示框源码分享
开发语言·rust
Dev _16 小时前
Python解释器安装在系统级目录导致命令行用户‌没有权限‌向该目录写入文件
开发语言·python
程序猿老A17 小时前
轻量应用服务器怎么用phpcms搭建网站?
android
qq_4069810317 小时前
大白话rust系列08 当变量遮蔽遇上引用,探究变量遮蔽的本质
开发语言·后端·rust
喵了几个咪17 小时前
GoWind ShortPlay 拆解:不用模型的推荐闭环——埋点回流、三路召回与已看去重
开发语言·后端·微服务·golang·gowind
谙弆悕博士17 小时前
用 C 写了个 Windows 聊天室,踩了几个多线程和控制台的坑
c语言·开发语言·c++·windows·网络编程·聊天室
Canmag 兴隆磁性17 小时前
回复磁导率 μ_R:永磁体在动态磁路中的关键参数
开发语言·学习·r语言·永磁材料·充磁·磁导率