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

本文关键字:使用率 增加 cpu 100% 线程 无限循环 | 更新日期: 2023-09-27 18:20:21

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

我将连接的客户端保存在内存中(dictionary对象),当有新消息发送到客户端时,我会将此消息写入接收方客户端的消息数组。客户端需要发送一个请求来获得自己的消息,我将此请求保存在内存中的一个数组中。

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

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

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

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

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

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

一切都很好,直到我注意到CPU使用率及时达到100%。(在第一个连接的客户端之后的分钟内)在第一个请求开始时,一切似乎都很正常,我的意思是,在向客户端返回响应时,CPU使用率不高于10%。但随着时间的推移,即使有两个客户端,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
          }
      }    
   }
}

我希望我能解释一下情况,我也愿意接受任何建议(比如以不同的方式实施)

如果你能帮我解决这个问题,我将不胜感激,感谢

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

正如一般最佳实践注释而不是直接回答一样,不建议在消息接收器线程中编写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。

如果做不到这一点,您是否考虑过异步双向消息传递的第三方解决方案?我已经在.NET应用程序中成功地使用了RabbitMQ(AMQP)来处理高吞吐量消息。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<Type, ILogger> TypeToLoggerMap = new Dictionary<Type, ILogger>();
    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?