当前位置: 首页 > news >正文

skynet Monitor 线程详解

目录

  1. 概述
  2. 主要职责
  3. 死循环检测原理
  4. 代码实现
  5. 工作流程
  6. 监控器数据结构
  7. 与 Worker 线程的交互

概述

Monitor 线程是 Skynet 的监控线程,负责监控所有 Worker 线程的运行状态,检测服务是否陷入死循环。Monitor 线程通过定时检查每个 Worker 的执行状态,发现异常时记录日志,帮助开发者定位问题。

特点:

  • 独立线程运行,不参与消息处理
  • 每隔约 5 秒检查一次所有 Worker
  • 通过版本号机制检测死循环
  • 记录死循环服务的详细信息

文件位置:skynet/skynet-src/skynet_start.c

线程数量: 1 个


主要职责

1. 监控 Worker 线程

功能: 检查每个 Worker 线程是否正常工作

检查内容:

  • Worker 是否在处理消息
  • Worker 是否长时间未更新状态
  • Worker 处理的消息是否完成

2. 死循环检测

功能: 检测服务是否陷入死循环

检测机制:

  1. Worker 每处理一条消息,版本号递增
  2. Monitor 定期检查版本号是否变化
  3. 如果 5 秒内版本号未变化,判定为死循环
  4. 记录死循环服务的日志信息

3. 运行时间统计

功能: 统计服务的运行时间

统计内容:

  • 服务处理消息的总时间
  • 服务最后活跃时间
  • 服务是否超时

死循环检测原理

版本号机制

┌─────────────────────────────────────────────────────────────┐ │ 死循环检测原理 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Worker 线程处理消息时: │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 1. 从消息队列取出消息 │ │ │ │ 2. 更新监控器: version++ │ │ │ │ 3. 记录开始时间: start_time = now │ │ │ │ 4. 调用服务回调函数 │ │ │ │ 5. 更新监控器: version++ │ │ │ │ 6. 记录结束时间: end_time = now │ │ │ │ 7. 清空监控器 │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ Monitor 线程检查时: │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 1. 遍历所有 Worker 的监控器 │ │ │ │ 2. 检查 version 是否变化 │ │ │ │ 3. 检查 start_time 是否超时 │ │ │ │ 4. 如果 5 秒内 version 未变化 → 死循环 │ │ │ │ 5. 如果 5 秒内 start_time 未结束 → 超时 │ │ │ │ 6. 记录日志信息 │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘

检测流程

时间轴: T=0s T=1s T=2s T=3s T=4s T=5s │ │ │ │ │ │ ├───────┼───────┼───────┼───────┼───────┤ │ │ │ │ │ │ Monitor │ │ │ │ │ 检查 │ │ │ │ │ version=1 │ │ │ │ │ │ │ │ Worker │ │ │ 处理消息1 │ │ │ version=2 │ │ │ │ │ │ Worker │ │ 处理消息2 │ │ version=3 │ │ │ │ Worker │ 处理消息3 │ version=4 │ │ Monitor 检查 version=4 正常 ✓

死循环场景

T=0s T=1s T=2s T=3s T=4s T=5s │ │ │ │ │ │ ├───────┼───────┼───────┼───────┼───────┤ │ │ │ │ │ │ Monitor │ │ │ │ │ 检查 │ │ │ │ │ version=1 │ │ │ │ │ │ │ │ Worker │ │ │ 处理消息1 │ │ │ version=2 │ │ │ │ │ │ Worker │ │ 死循环! │ │ version=2 │ │ (未变化) │ (未变化) │ (未变化) │ │ Monitor 检查 version=2 死循环 ✗

代码实现

Monitor 线程主函数

文件:skynet/skynet-src/skynet_start.c

位置:skynet_start.c:202-226

staticvoid*thread_monitor(void*p){structmonitor*m=p;inti;intn=m->count;// Worker 线程数量skynet_initthread(THREAD_MONITOR);pthread_setname_np(pthread_self(),"monitor");// 设置线程名称for(;;){CHECK_ABORT// 检查是否应该退出// 遍历所有 worker 的监控器for(i=0;i<n;i++){skynet_monitor_check(m->m[i]);// 检查监控器}// 休眠 5 秒(分成 5 次,每次 1 秒,以便快速响应退出)for(i=0;i<5;i++){CHECK_ABORT// 检查是否应该退出sleep(1);// 休眠 1 秒}}returnNULL;}

监控器检查函数

文件:skynet/skynet-src/skynet_monitor.c

voidskynet_monitor_check(structskynet_monitor*sm){inti;for(i=0;i<5;i++){// 检查每个 workeruint32_tv=sm->version[i];// 读取版本号uint32_thandle=sm->source[i];// 读取服务句柄if(v!=sm->check_version[i]){// 版本号变化了,更新检查版本号sm->check_version[i]=v;sm->check_source[i]=handle;continue;}if(handle){// 版本号未变化,但 handle 不为空// 说明服务还在处理消息uint32_thandle=sm->source[i];uint32_thandle_s=sm->check_source[i];if(handle_s!=0&&handle==handle_s){// 同一个服务 5 秒内版本号未变化// 判定为死循环fprintf(stderr,"Deadloop detected in service %d\n",handle);}}}}

工作流程

┌─────────────────────────────────────────────────────────────┐ │ Monitor 线程工作流程 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 线程启动 │ │ │ │ │ ▼ │ │ 初始化线程局部存储 │ │ skynet_initthread(THREAD_MONITOR) │ │ │ │ │ ▼ │ │ 主循环 │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ while (!quit) { │ │ │ │ │ │ │ │ │ ├─ CHECK_ABORT │ │ │ │ │ (检查是否应该退出) │ │ │ │ │ │ │ │ │ ├─ 遍历所有 Worker 的监控器 │ │ │ │ │ for (i=0; i<worker_count; i++) { │ │ │ │ │ │ │ │ │ │ │ └─ skynet_monitor_check(m->m[i]) │ │ │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ │ 读取监控器状态 │ │ │ │ │ - version[i] │ │ │ │ │ - source[i] │ │ │ │ │ - check_version[i] │ │ │ │ │ - check_source[i] │ │ │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ │ 比较版本号 │ │ │ │ │ if (version != check_version) │ │ │ │ │ 正常,更新检查版本号 │ │ │ │ │ else │ │ │ │ │ 死循环,记录日志 │ │ │ │ │ } │ │ │ │ │ │ │ │ │ ├─ 休眠 5 秒 │ │ │ │ │ for (i=0; i<5; i++) { │ │ │ │ │ CHECK_ABORT │ │ │ │ │ sleep(1); │ │ │ │ │ } │ │ │ │ │ │ │ │ │ └─ 继续循环 │ │ │ │ } │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ ▼ │ │ 线程退出 │ │ │ │ │ ▼ │ │ return NULL │ │ │ └─────────────────────────────────────────────────────────────┘

监控器数据结构

skynet_monitor 结构

文件:skynet/skynet-src/skynet_monitor.c

structskynet_monitor{intversion;// Worker 版本号intcheck_version;// Monitor 检查的版本号uint32_tsource;// 当前处理的服务句柄uint32_tcheck_source;// 检查时的服务句柄intwork_version;// 工作版本号intwork_source;// 工作源};

字段说明

字段类型说明
versionintWorker 每处理一条消息递增,初始为 0
check_versionintMonitor 上次检查的版本号
sourceuint32_tWorker 当前处理的服务句柄
check_sourceuint32_tMonitor 上次检查的服务句柄
work_versionint工作版本号(用于调试)
work_sourceint工作源(用于调试)

与 Worker 线程的交互

Worker 线程更新监控器

文件:skynet/skynet-src/skynet_monitor.c

voidskynet_monitor_trigger(structskynet_monitor*sm,intsource){sm->version=sm->version+1;// 版本号递增sm->source=source;// 设置当前服务句柄}

Worker 线程清空监控器

文件:skynet/skynet-src/skynet_monitor.c

voidskynet_monitor_delete(structskynet_monitor*sm){sm->source=0;// 清空服务句柄}

交互时序图

Worker 线程 Monitor 线程 │ │ │ 1. 取出消息 │ │ │ │ 2. skynet_monitor_trigger() │ │ ├─ version++ │ │ └─ source = handle │ │ │ │ 3. 处理消息 │ │ (可能需要 0.1-5 秒) │ │ │ │ 4. skynet_monitor_delete() │ │ └─ source = 0 │ │ │ │─────────────────────────────▶│ │ │ 5. skynet_monitor_check() │ │ ├─ version == check_version? │ │ ├─ 是 → 正常 │ │ └─ 否 → 死循环 │ │ │ 6. 取出下一条消息 │ │ │ │ 7. skynet_monitor_trigger() │ │ ├─ version++ │ │ └─ source = handle │ │ │ │─────────────────────────────▶ │ │ │ 8. skynet_monitor_check() │ │ ├─ 更新 check_version │ │ └─ 正常

检测超时

除了死循环检测,Monitor 还可以检测服务处理超时。

超时检测逻辑

if(handle&&check_source==handle){// 同一个服务 5 秒内版本号未变化// 判定为死循环fprintf(stderr,"Deadloop detected in service %d\n",handle);// 检查是否超时uint32_ttime_cost=current_time-start_time;if(time_cost>5000){// 超过 5 秒fprintf(stderr,"Service %d timeout (%dms)\n",handle,time_cost);}}

配置和调优

默认配置

配置项默认值说明
检查间隔5 秒Monitor 每隔 5 秒检查一次
超时阈值5 秒超过 5 秒未完成视为死循环

调整检查间隔

如果需要更频繁的检查,可以修改代码:

// 原代码:休眠 5 秒for(i=0;i<5;i++){CHECK_ABORTsleep(1);}// 修改为:休眠 2 秒for(i=0;i<2;i++){CHECK_ABORTsleep(1);}

注意: 更频繁的检查会增加 CPU 开销。


日志示例

正常情况

[时间戳] Service 0x00010002: Processing message... [时间戳] Service 0x00010002: Message processed (2ms)

死循环检测

[时间戳] Deadloop detected in service 0x00010002 [时间戳] Service 0x00010002: Last action: skynet.call [时间戳] Service 0x00010002: Current function: handle_message

总结

Monitor 线程的核心作用:

  1. 监控: 监控所有 Worker 线程的运行状态
  2. 检测: 检测服务死循环和超时
  3. 记录: 记录异常服务的详细信息
  4. 帮助: 帮助开发者定位问题

关键机制:

  • 版本号机制:Worker 更新版本号,Monitor 检查版本号
  • 定时检查:每隔 5 秒检查一次
  • 非侵入式:不影响 Worker 的正常工作

与其他线程的关系:

  • Worker 线程:被监控对象
  • 主线程:创建 Monitor 线程
  • 独立运行:不参与消息处理
http://www.cnnetsun.cn/news/1333412.html

相关文章:

  • 2026笔记本Windows电源管理:硬盘休眠与PCIe链路
  • Python 实战:基于朴素贝叶斯的中文评价情感分析(好评 / 差评自动识别)| 附完整可运行代码
  • 一文详解Diffusion Policy
  • C++11中智能指针:shared_ptr的引用计数是线程安全的吗?
  • 不懂代码,我用AI编程给5岁女儿开发了个流光画板(带你一步一步设计一个属于自己的流光画板)
  • VScode快捷键
  • 小白从零开始勇闯人工智能:LangChain 入门指南(下)
  • AI时代的教育“外包”:中国家长将作业辅导交给机器
  • 2026年课程论文降AI率工具推荐:便宜好用才是硬道理
  • 软件系统安全赛初赛misc题-steganography wp
  • 东方仙盟・神识共创共生,智启万象—架构思路—未来之窗行业应用跨平台架构
  • linux-安装配置jdk mysql redis elasticsearch
  • Python 之程序截图的几种方式(含chromedriver下载链接)
  • android studio项目 gradle-xx-bin.zip下载失败或很慢的解决方法
  • AperiSolve 开源项目教程
  • 企业决策视角下微服务全链路性能瓶颈分析平台对比及实践指南
  • 【C语言】程序环境与预处理
  • SCUT_thesis项目:解决长章名换行后不居中的排版问题
  • PowerPlatformConnectors三大类型深度对比:自定义、认证与独立发布者连接器怎么选?
  • Pleaserun vs 手动编写init脚本:效率提升10倍的秘密
  • dbblog部署教程:Docker容器化部署与服务器配置全流程
  • AniVu BitTorrent下载功能深度测评:速度与稳定性全面测试
  • DC-TTS语音合成效果对比:LJ Speech与KSS数据集实测
  • 提升Haskell开发效率:ghcid高级功能与实用技巧
  • Saasify:让API变现从未如此简单!一站式实现API商业化的终极指南
  • Ikemen-GO开发者指南:用Go语言构建自定义格斗游戏引擎
  • GoMLX未来路线图:即将发布的5大令人期待的功能
  • lidar_camera_calib常见问题排查:从数据准备到结果验证的全流程避坑指南
  • 如何用tinygrad实现高效目标检测:YOLO和RetinaNet完整指南
  • 如何用tinygrad实现高效全局优化:从模拟退火到能量最小化的完整指南