线程无限循环中 CPU 使用率增加至 100%

发布于 2024-12-27 09:30:25 字数 1706 浏览 0 评论 0原文

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

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

我正在使用异步 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%。但即使有 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
          }
      }    
   }
}

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

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

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.

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).

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

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:

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.

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.

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.

Here is the QueueCometWaitRequest_WaitCallback() method:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

终弃我 2025-01-03 09:30:25

就像一般的最佳实践评论而不是直接答案一样 - 不建议在消息接收器线程内编写 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 意味着当收到消息时您会收到一个事件,然后可以在后台线程上处理该消息。

此致,

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:

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.

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.

Best regards,

赠我空喜 2025-01-03 09:30:25

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

如果静态使用,字典对象不是线程安全的。如果将其用作静态成员,则需要创建一个 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?

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.

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.

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.

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?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文