c# 如何用 EventSource 和 EventListener 监控内部并发事件

来源:这里教程网 时间:2026-02-21 17:38:13 作者:

EventSource 是什么,为什么不能直接 new EventListener

EventSource
是 .NET 提供的轻量级、高性能事件发布机制,专为生产环境诊断设计;它本身不负责监听,只负责「写」。而
EventListener
是抽象基类,必须继承并重写
OnEventSourceCreated
OnEventWritten
才能接收事件——你不能直接
new EventListener()
,否则毫无作用。

如何让自定义 EventListener 捕获并发相关事件(如 TaskScheduler、ThreadPool)

.NET 运行时自带多个内部

EventSource
,比如
System.Threading.Tasks.TplEventSource
System.Runtime.ThreadPoolEventSource
,它们默认是禁用的。要监听,必须在
OnEventSourceCreated
中显式启用对应事件源,并指定关键词(keyword)和等级(level)。

关键词决定捕获哪类事件:例如
TaskScheduler
事件常用
EventSource.Settings.TaskScheduler
(实际值为 0x10),
ThreadPool
EventSource.Settings.ThreadPool
(0x20)
等级至少设为
EventLevel.Verbose
才能看到调度细节(如任务入队、线程唤醒)
必须调用
EnableEvents(eventSource, level, keywords)
,且该调用需在
OnEventSourceCreated
内完成,延迟启用会丢事件
public class ConcurrencyEventListener : EventListener
{
    protected override void OnEventSourceCreated(EventSource eventSource)
    {
        if (eventSource.Name == "System.Threading.Tasks.TplEventSource")
        {
            EnableEvents(eventSource, EventLevel.Verbose, (EventKeywords)0x10); // TaskScheduler
        }
        else if (eventSource.Name == "System.Runtime.ThreadPoolEventSource")
        {
            EnableEvents(eventSource, EventLevel.Verbose, (EventKeywords)0x20); // ThreadPool
        }
    }
    protected override void OnEventWritten(EventWrittenEventArgs eventData)
    {
        if (eventData.EventName is "ScheduledTask" or "ThreadRequested" or "WorkerThreadStart")
        {
            Console.WriteLine($"[{eventData.EventSource.Name}] {eventData.EventName}: {string.Join(", ", eventData.PayloadNames.Zip(eventData.Payload, (n, v) => $"{n}={v}"))}");
        }
    }
}

常见漏掉的并发事件和调试陷阱

很多人以为启用了

TplEventSource
就能看见所有 Task 行为,但实际有三个关键限制:

TaskCreationOptions.RunContinuationsAsynchronously
Task.Run
创建的任务才触发
ScheduledTask
;同步 continuation(如
ContinueWith
默认行为)不会发事件
ThreadPoolEventSource
ThreadRequested
只在“线程不足需扩容”时发出,空闲线程复用过程无事件
若程序启动后才创建
ConcurrencyEventListener
实例,此前已发生的调度事件(尤其是 AppDomain 初始化阶段的)完全丢失——必须尽早 new 并保持存活
事件负载高时(如每秒数千任务),
OnEventWritten
是同步调用,阻塞会导致事件被丢弃;生产环境应异步缓冲或限流处理

验证是否真收到了并发事件的最简方法

不要依赖日志滚动——写个可复现的最小触发片段,配合断点或计数器确认:

var listener = new ConcurrencyEventListener();
// 确保 listener 实例不被 GC(比如存为 static 字段)
Task.Run(() => Thread.Sleep(1)); // 触发 ThreadPool + Tpl 调度事件
Task.Factory.StartNew(() => { }, TaskCreationOptions.PreferFairness);
// 等待一小会儿再 Dispose,避免事件还在管道中
await Task.Delay(100);
listener.Dispose();

如果

OnEventWritten
完全没被调用,优先检查
EventSource.Name
是否拼错(大小写敏感)、.NET 版本是否支持(
TplEventSource
在 .NET Core 2.1+ 稳定,.NET Framework 4.6.2+ 有但字段名不同)。

并发监控不是开箱即用的开关,每个

EventSource
都有自己隐含的触发边界和采样逻辑,想看到某类行为,得先确认它确实属于该事件源的输出范畴。

相关推荐