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

时间:2022-11-27 20:57:03

I am implementing a web based chat platform in ASP.NET Web Application, and I use technique similar to long polling. I mean I keep each web request from client for a specific time period(timeout) or until new message arrives, and then response is sent to the client.

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

I keep connected clients in memory(dictionary object) and when ever new message is sent to a client, I write this message into receiver client's messages array. Client needs to send a request to get his own messages, and I keep this request in an array in memory.

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

I am using asynchronous http handler for listenings client request, I am keeping web requests in an array in memory. I use threads to check for new messages continously from memory (in dictionary which is created for each client).

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

I do not use .net thread pool threads to check for new messages or timed out web requests.I create threads like this:

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

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

In each thread's QueueCometWaitRequest_WaitCallback method I am in an infinite while loop:

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

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

In this method, I am checking for web request time out or new message for each Web Request which is also kept in an array in memory.

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

Everything was working good until I noticed that CPU usage is reaching up to 100% in time. (in minutes after the first connected client) At the beginning of first request everything seems to be normal, I mean the CPU usage is not higher than 10% while returning a response to the client. But in time even with 2 clients the CPU usage is increasing up to 100%. It seems CPU usage is 100% only when writing to a response for a client request. If no client is left then everything return to a normal (CPU usage is about 0%) until new web request is done by a client.

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

I don't know the threads in detail, but I am suspicious about the new threads which I created and works infinitely. It is like operating system gives them more CPU usage and resource in time since they are working all the time, and this Thread.Sleep(100) is not working.

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

Here is the QueueCometWaitRequest_WaitCallback() method:

这是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
          }
      }    
   }
}

I hope I could explain the situation, and I am open to any suggestion as well (like implementing in a different way)

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

If you can help me with this problem I will appreciate gratefully, Thanks

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

2 个解决方案

#1


9  

Just as a general best-practices comment as opposed to direct answer - it's not advisbable to write a Thread.Sleep(100) inside your message receiver thread. A better method would be to use Thread.Join as previously mentioned or ManualResetEvent wait handles. For instance, you could code like this:

就像一般的最佳实践注释而不是直接回答一样 - 在消息接收器线程中编写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();
        }
    }        
} 

This would ensure that your thread uses 0% CPU while waiting and only consumes CPU when there is work to do.

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

Failing that have you thought about a third party solution to asynchronous bi-directional messaging? I have used RabbitMQ (AMQP) with great success in .NET applications to handle high throughput messaging. The API for RabbitMQ means you get an event back when a message has been received which can then be processed on a background thread.

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

Best regards,

#2


0  

I keep connected clients in memory(dictionary object)

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

Dictionary objects aren't thread safe if used statically. If using it as a static member then you need to create a Lock statement.

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

Here's an example lifted from a Log4Net LoggerFactory class...Notice that the TypeToLoggerMap is a dictionary object and when it's referenced vai the GetLogger method, a Lock statement is used.

这是一个从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;
        }
    }
}

Check this article out - this is where I discovered the above info about Dictionary objects.

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

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

As a side note, have you considered using SignalR for your project?

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

#1


9  

Just as a general best-practices comment as opposed to direct answer - it's not advisbable to write a Thread.Sleep(100) inside your message receiver thread. A better method would be to use Thread.Join as previously mentioned or ManualResetEvent wait handles. For instance, you could code like this:

就像一般的最佳实践注释而不是直接回答一样 - 在消息接收器线程中编写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();
        }
    }        
} 

This would ensure that your thread uses 0% CPU while waiting and only consumes CPU when there is work to do.

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

Failing that have you thought about a third party solution to asynchronous bi-directional messaging? I have used RabbitMQ (AMQP) with great success in .NET applications to handle high throughput messaging. The API for RabbitMQ means you get an event back when a message has been received which can then be processed on a background thread.

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

Best regards,

#2


0  

I keep connected clients in memory(dictionary object)

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

Dictionary objects aren't thread safe if used statically. If using it as a static member then you need to create a Lock statement.

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

Here's an example lifted from a Log4Net LoggerFactory class...Notice that the TypeToLoggerMap is a dictionary object and when it's referenced vai the GetLogger method, a Lock statement is used.

这是一个从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;
        }
    }
}

Check this article out - this is where I discovered the above info about Dictionary objects.

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

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

As a side note, have you considered using SignalR for your project?

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