在线程中的无限循环中,cpu使用率增加到100%

我在ASP.NET Web应用程序中实现了一个基于Web的聊天平台,我使用类似于长轮询的技术。 我的意思是我将来自客户端的每个Web请求保留特定时间段(超时)或直到新消息到达,然后将响应发送到客户端。

我将连接的客户端保留在内存中(字典对象),当有新消息发送到客户端时,我将此消息写入接收方客户端的消息数组中。 客户端需要发送请求以获取自己的消息,并将此请求保存在内存中的数组中。

我正在使用异步http处理程序来监听客户端请求,我将Web请求保存在内存中的数组中。 我使用线程从内存中连续检查新消息(在为每个客户端创建的字典中)。

我不使用.net线程池线程来检查新消息或超时web请求。我创建这样的线程:

System.Threading.Thread t = new Thread(new ThreadStart(QueueCometWaitRequest_WaitCallback)); t.IsBackground = false; t.Start(); 

在每个线程的QueueCometWaitRequest_WaitCallback方法中,我处于无限循环中:

 while (true) { ... Thread.Sleep(100); } 

在这个方法中,我正在检查Web请求超时或每个Web请求的新消息,它也保存在内存中的数组中。

一切都很顺利,直到我注意到CPU使用率达到了100%的时间。 (在第一个连接的客户端之后的几分钟内)在第一个请求开始时,一切似乎都正常,我的意思是在向客户端返回响应时CPU使用率不高于10%。 但即使有2个客户端,CPU使用率也会增加到100%。 只有在写入客户端请求的响应时,CPU使用率似乎是100%。 如果没有客户端,那么一切都恢复正常(CPU使用率约为0%),直到客户端完成新的Web请求。

我不太详细地知道线程,但我怀疑我创建的新线程并且无限地工作。 这就像操作系统因为它们一直在工作而及时为它们提供更多的CPU使用和资源,并且这个Thread.Sleep(100)不起作用。

这是QueueCometWaitRequest_WaitCallback()方法:

 void QueueCometWaitRequest_WaitCallback() { while (true) { if (processRequest.Length == 0) { Thread.Sleep(100); } else { for (int i = 0; i < processRequest.Length; i++) { Thread.Sleep(100); // below I am checking for new message or request time out ................. ................. // If new message or time out I write to response } } } } 

我希望我能解释一下情况,我也对任何建议持开放态度(比如以不同的方式实施)

如果你能帮助我解决这个问题,我将非常感谢,谢谢

就像一般的最佳实践注释而不是直接回答一样 – 在消息接收器线程中编写Thread.Sleep(100)是不可取的。 一个更好的方法是使用前面提到的Thread.Join或ManualResetEvent等待句柄。 例如,您可以像这样编码:

 private ManualResetEvent waitHandle; private object syncRoot = new object(); private bool isRunning = false; void CreateThread() { this.waitHandle = new ManualResetEvent(false); isRunning = true; // Set to false to kill the thread System.Threading.Thread t = new Thread(new ThreadStart(QueueCometWaitRequest_WaitCallback)); t.IsBackground = false; t.Start(); } void PushData() { // On incoming data, push data into the processRequest queue and set the waithandle lock(syncRoot) { processRequest.Add(/* ... your data object to process. Assumes this is a queue */); waitHandle.Set(); // Signal to the thread there is data to process } } void QueueCometWaitRequest_WaitCallback() { while (isRunning) { // Waits here using 0% CPU until the waitHandle.Set is called above this.waitHandle.WaitOne(); // Ensures no-one sets waithandle while data is being processed and // subsequently reset lock(syncRoot) { for (int i = 0; i < processRequest.Length; i++) { // Process the message. // What's the type of processRequest? Im assuming a queue or something } // Reset the Waithandle for the next requestto process this.waitHandle.Reset(); } } } 

这将确保您的线程在等待时使用0%CPU,并且只有在有工作要做时才消耗CPU。

如果不这样,您是否考虑过针对异步双向消息传递的第三方解决方案? 我使用RabbitMQ (AMQP)在.NET应用程序中取得了巨大成功,可以处理高吞吐量的消息传递。 RabbitMQ的API意味着您在收到消息后会收到一个事件,然后可以在后台线程上处理。

最好的祝福,

我在内存中保持连接的客户端(字典对象)

如果静态使用,字典对象不是线程安全的。 如果将它用作静态成员,则需要创建一个Lock语句。

这是一个从Log4Net LoggerFactory类中提取的示例…请注意,TypeToLoggerMap是一个字典对象,当它引用了GetLogger方法时,使用了一个Lock语句。

 public static class LoggerFactory { public static ILogger GetLogger(Ninject.Activation.IContext context) { return GetLogger(context.Request.Target == null ? typeof(ILogger) : context.Request.Target.Member.DeclaringType); } private static readonly Dictionary TypeToLoggerMap = new Dictionary(); private static ILogger GetLogger(Type type) { lock (TypeToLoggerMap) { if (TypeToLoggerMap.ContainsKey(type)) return TypeToLoggerMap[type]; ILogger logger = new Logger(type); TypeToLoggerMap.Add(type, logger); return logger; } } } 

查看这篇文章 – 这是我发现有关Dictionary对象的上述信息的地方。

https://www.toptal.com/dot-net/hunting-high-cpu-usage-in-dot-net

作为旁注,您是否考虑过将SignalR用于您的项目?