线程分组与导出
基本信息
| 属性 | 值 |
|---|---|
| 分组器 | me.lucko.spark.common.sampler.ThreadGrouper |
| 导出器 | me.lucko.spark.common.sampler.ThreadDumper |
| 相关配置 | backgroundProfilerThreadGrouper、backgroundProfilerThreadDumper |
采样器采集到的是「一堆线程各自的时间」,需要两步加工才有意义:ThreadDumper 决定采哪些线程,ThreadGrouper 决定采到的线程怎么合并成报告里的几行。
ThreadGrouper(线程归并)
ThreadGrouper.parseConfigSetting(String setting)(ThreadGrouper.java:67-76):
switch (setting) {
case "as-one": return AS_ONE;
case "by-name": return BY_NAME;
default: return BY_POOL;
}
| 配置值 | 常量 | 归并规则 |
|---|---|---|
as-one |
AS_ONE |
所有线程合并为单一分组 |
by-name |
BY_NAME |
按线程名归并(getGroup 返回 threadName) |
by-pool(默认) |
BY_POOL |
按线程池归并 —— 未匹配任何已知池名时落到此分支 |
注意 switch 的 default 落到 BY_POOL —— 任何拼错的配置值都会静默变成 by-pool,不报错。
在多线程服务器上这直接决定报告可读性:默认 by-pool 会把 Acceptor #0/Worker #0 这类 Netty/pool 线程合并成几行,而不是几十行独立条目;改成 by-name 则每个线程单列。
ThreadDumper(线程筛选)
ThreadDumper.parseConfigSetting(String setting)(ThreadDumper.java:71-81):
switch (setting) {
case "default": return null;
case "all": return ALL;
default:
Set<String> threadNames = Arrays.stream(setting.split(",")).collect(Collectors.toSet());
return new ThreadDumper.Specific(threadNames);
}
| 配置值 | 结果 |
|---|---|
default |
返回 null,由调用方回退到平台默认线程 |
all |
ThreadDumper.ALL —— threadBean.dumpAllThreads(false, false),导出全部线程 |
| 其他任意字符串 | 按 , 拆分为线程名集合,构造 ThreadDumper.Specific |
default 返回 null 是有意设计:调用方 BackgroundSamplerManager.startSampler() 判空后回退到 platform.getPlugin().getDefaultThreadDumper(),由平台决定「默认线程」是什么。
逗号分隔的多线程名是本配置的主要用法 —— 例如 "Server thread,Netty Server IO#0"。注意解析不 trim 空白,且分隔符固定为英文逗号。
1.7.10 的平台默认线程
SparkPlugin.getDefaultThreadDumper() 的接口默认实现返回 ThreadDumper.ALL(SparkPlugin.java:105-107),但 1.7.10 两个插件都覆写了它:
| 平台 | 默认线程导出器 | 捕获时机 |
|---|---|---|
| 服务端 | ThreadDumper.GameThread,在 Forge1710ServerSparkPlugin 构造时 setThread(Thread.currentThread()) |
Forge1710ServerSparkPlugin.java:83-84 |
| 客户端 | ThreadDumper.Specific(minecraft.mcThread) |
Forge1710ClientSparkPlugin.java:56 |
即 1.7.10 上默认只采主线程(服务端)或 Minecraft 客户端线程,与其他平台默认导出全部线程的行为不同。要在服务端采全部线程需显式设 backgroundProfilerThreadDumper: "all"。
服务端之所以要在构造时捕获当前线程:插件在 FMLServerStartingEvent 中注册,此时 Thread.currentThread() 即服务端主线程。
客户端主线程的访问变换器
客户端访问 minecraft.mcThread 需要 FMLAT。spark-forge1710/src/main/resources/META-INF/spark_at.cfg 内容仅一行:
public net.minecraft.client.Minecraft field_152352_aC # mcThread
由 spark-forge1710/build.gradle 的 jar { manifest { attributes 'FMLAT': 'spark_at.cfg' } } 挂载。详见 访问变换器。
数值
| 数值名 | 值 | 来源 |
|---|---|---|
ThreadGrouper 合法值 |
as-one / by-name / 其他(含 by-pool) |
ThreadGrouper.parseConfigSetting |
ThreadDumper 特殊值 |
default / all / 其他=逗号分隔线程名 |
ThreadDumper.parseConfigSetting |
| 线程名分隔符 | ,(不 trim) |
ThreadDumper.parseConfigSetting |
| 1.7.10 服务端默认线程 | 主线程(构造时捕获) | Forge1710ServerSparkPlugin |
| 1.7.10 客户端默认线程 | minecraft.mcThread |
Forge1710ClientSparkPlugin |