是否有一种方法可以从.NET中获取关于待执行任务(C# 5或更高版本,所以是异步后/等待任务)数量的数据,以及类似的度量,用于诊断发生在生产服务器上的问题?
我正在讨论的情况是一个异步-全方位下降系统(例如,一个大规模的并行套接字服务器,其中每个请求从一开始就运行异步),其中初始任务要么产生多个任务,每个任务都需要时间处理(或者每次启动更多的任务),要么产生一些任务,其中一些块(比如第三方代码)和一些任务异步工作。我见过两种很难有效诊断的情况:
我试着编写了一个简单的测试,但是没有明显的方法来限制执行器的数量和测试所需要创建的任务的数量,这使得解析信息变得非常困难。通过尝试注销调试信息,也很难不干扰测试本身。我将继续尝试创建一个更好的测试用例,并在需要时修改我的问题。
根据我对问题和异步任务系统的理解,这两者实际上都是关于实际运行任务的执行者的争用。
第一种情况发生是因为创建的任务多于实际完成的任务,在这种情况下,一个挂起的任务计数器将有助于诊断这一点,甚至在负载足够高以锁定服务之前也是如此。
第二种情况发生是因为某个特定的任务集足够长,但随着时间的推移(有足够的负载),所有执行程序最终都同时运行这些任务。一旦完成,它就会处理一些任务,但很快就会被另一个长期运行的任务所取代。在这种情况下,挂起的任务计数器将是有用的,以及其他一些指标。
是否有任何可用的类型,或者是否有某种无文档/黑客的方式将一些代码移植到应用程序中开始/结束的每个任务的开始/结束,从而使其注销/测量这些事情,并在任务号爆炸时抛出警告?
发布于 2018-05-23 11:32:58
您可以从EventListener继承一个类来处理任务并行库生成的事件。也许,您可以以这种方式在ConcurrentDictionary中计算排队和正在运行的任务,并存储与任务关联的分析信息。但是,也有一些复杂的问题,例如任务ids的不唯一性或此分析的性能影响。
实例实现:
public class TplEventListener : EventListener
{
static readonly Guid _tplSourceGuid = new Guid("2e5dba47-a3d2-4d16-8ee0-6671ffdcd7b5");
readonly EventLevel _handledEventsLevel;
public TplEventListener(EventLevel handledEventsLevel)
{
_handledEventsLevel = handledEventsLevel;
}
protected override void OnEventSourceCreated(EventSource eventSource)
{
if (eventSource.Guid == _tplSourceGuid)
EnableEvents(eventSource, _handledEventsLevel);
}
protected override void OnEventWritten(EventWrittenEventArgs eventData)
{
if (eventData.EventSource.Guid != _tplSourceGuid)
return;
switch (eventData.EventId)
{
// TODO: Add case for each relevant EventId (such as TASKSCHEDULED_ID and TASKWAITBEGIN_ID)
// and explore relevant data (such as task Id) in eventData.Payload. Payload is described by
// eventData.PayloadNames.
// For event ids and payload meaning explore TplEtwProvider source code
// (https://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/TPLETWProvider.cs,183).
default:
var message = new StringBuilder();
message.Append(eventData.EventName);
message.Append("(");
message.Append(eventData.EventId);
message.Append(") { ");
if (!string.IsNullOrEmpty(eventData.Message))
{
message.Append("Message = \"");
message.AppendFormat(eventData.Message, eventData.Payload.ToArray());
message.Append("\", ");
}
for (var i = 0; i < eventData.Payload.Count; ++i)
{
message.Append(eventData.PayloadNames[i]);
message.Append(" = ");
message.Append(eventData.Payload[i]);
message.Append(", ");
}
message[message.Length - 2] = ' ';
message[message.Length - 1] = '}';
Console.WriteLine(message);
break;
}
}
}初始化并在每个new TplEventListener(EventLevel.LogAlways)中存储AppDomain,您将得到类似于以下内容的日志:
NewID(26) { TaskID =1} TaskScheduled(7) { Message =“TaskScheduler 1调度的任务1”,OriginatingTaskSchedulerID = 1,OriginatingTaskID = 0,TaskID = 1,CreatingTaskID = 0,TaskCreationOptions = 8192 } NewID(26) { TaskID =2} TraceOperationBegin(14) { TaskID = 2,OperationName = Task.ContinueWith:< SendAsync > b__0,RelatedContext =0} TaskStarted(8) { Message =“任务1正在执行”,OriginatingTaskSchedulerID = 1,OriginatingTaskID = 0,TaskID =1} AwaitTaskContinuationScheduled(12) { OriginatingTaskSchedulerID = 1,OriginatingTaskID = 0,ContinuwWithTaskId =2} NewID(26) { TaskID =3} TraceOperationBegin(14) { TaskID = 3,OperationName =异步:< Main > d__3,RelatedContext =0} NewID(26) { TaskID =4} TaskWaitBegin(10) { Message =“开始等待(2) on Task 4”,OriginatingTaskSchedulerID = 1,OriginatingTaskID = 0,TaskID = 4,Behavior = 2,ContinueWithTaskID =3} TaskWaitBegin(10) { Message =“开始等待(1) on Task 3”,OriginatingTaskSchedulerID = 1,OriginatingTaskID = 0,TaskID = 3,Behavior = 1,ContinueWithTaskID =0} TraceSynchronousWorkBegin(17) { TaskID = 1,功=2} TraceSynchronousWorkEnd(18) {功=2} TraceOperationEnd(15) { TaskID = 1,Status =1} RunningContinuation(20) { TaskID = 1,Object =0} TaskCompleted(9) { Message =“任务1已完成”,OriginatingTaskSchedulerID = 1,OriginatingTaskID = 0,TaskID = 1,IsExceptional = False }
有关更多信息,请查看:
发布于 2018-05-21 19:59:41
在生产环境中,Metrics.NET库非常方便。您可以对代码进行测试,并定期将收集的数据写入本地文件或数据库。在开发环境中,您可以使用Visual分析器来探索CPU和地址空间的使用情况。参见Stephen的使用Visual 2012的.NET内存分配分析文章。
Metrics.NET维基的相关摘录:
Metrics.NET库提供了五种可以记录的度量类型:
和仪表实例:
public class SampleMetrics
{
private readonly Timer timer = Metric.Timer("Requests", Unit.Requests);
private readonly Counter counter = Metric.Counter("ConcurrentRequests", Unit.Requests);
public void Request(int i)
{
this.counter.Increment();
using (this.timer.NewContext()) // measure until disposed
{
// do some work
}
this.counter.Decrement();
}
}有关更多信息,请查看:
发布于 2018-05-26 11:06:25
似乎莱昂尼德·瓦西列夫的回答对你来说已经足够了,但我想分享我的经验,当我遇到任务失败或花费比平时更长的时候。
这里最大的罪魁祸首是*上下文切换*,启动的任务越多,CPU就越需要跟踪上下文。相信我,对于一个CPU来说,做100个重任务比做100个轻任务是有效的。
对我来说,诀窍是根据提交的请求(我们使用消息队列)分析负载模式,并保持一个基于patterns.and的亮点--我还在每个任务结束时手工收集了ForceGC。
我知道你在找工具来帮助分析,我想这会有帮助的。我相信,对于这些问题,最好首先关注传入流量,而不是分析我们对这些流量做了什么。
https://stackoverflow.com/questions/50398254
复制相似问题