Windows服务应用程序上的高CPU使用率

时间:2010-08-19 18:43:18

标签: c# .net multithreading

我最近开始了一项新工作,这里有一个Windows服务,它使用来自私有Windows队列的消息。此服务仅在上午9点至下午6点消耗消息。因此,在晚上7点到8点59分期间,它会在队列中累积大量消息。当它在晚上9点开始处理时,服务的CPU使用率会很高(98%,99%),这会影响服务器的性能。

这个服务使用线程来处理队列的消息,但是在我有点迷失之前我从未使用过线程。

以下是我确信发生这种情况的代码部分:

private Thread[] th;

//in the constructor of the class, the variable th is initialized like this:
this.th = new Thread[4];

//the interval of this method calling is 1sec, it only goes high cpu usage when there is a lot of messages in the queue
public void Exec()
{
    try
    {
        AutoResetEvent autoEvent = new AutoResetEvent(false);
        int vQtd = queue.GetAllMessages().Length;

        while (vQtd > 0)
        {
            for (int y = 0; y < th.Length; y++)
            {
                if (this.th[y] == null || !this.th[y].IsAlive)
                {
                    this.th[y] = new Thread(new ParameterizedThreadStart(ProcessMessage));
                    this.th[y].Name = string.Format("Thread_{0}", y);
                    this.th[y].Start(new Controller(queue.Receive(), autoEvent));
                    vQtd--;
                }
            }
        }
    }
    catch (Exception ex)
    {
        ExceptionPolicy.HandleException(ex, "RECOVERABLE");
    }
}
编辑:我正在尝试Brian Gideon发布的第二种方法。但老实说:我对代码非常困惑,而且我对它正在做的事情一无所知。

我没有改变创建4个线程的方式和我展示的其他代码,只是改变了我的Exec(exec是在每天上午9点到下午6点时调用的方法)方法:

public void Exec()
    {
        try
        {
            AutoResetEvent autoEvent = new AutoResetEvent(false);
            int vQtd = queue.GetAllMessages().Length;

            while (vQtd > 0)
            {

                for (int i = 0; i < 4; i++)
                        {
                            var thread = new Thread(
                                (ProcessMessage) =>
                            {
                                while (true)
                                {
                                    Message message = queue.Receive();
                                    Controller controller = new Controller(message, autoEvent);
                                    //what am I supposed to do with the controller?
                                }
                            });
                        thread.IsBackground = true;
                        thread.Start();
                        }
        vQtd--;

            }
        }
        catch (Exception ex)
        {
            ExceptionPolicy.HandleException(ex, "RECOVERABLE");
        }
    }

3 个答案:

答案 0 :(得分:2)

哎哟。我必须诚实。这不是一个很好的设计。它很可能围绕while循环旋转,等待先前的线程完成处理。这是一种更好的方法。请注意,4个线程只创建一次并永远挂起。下面的代码使用.NET 4.0 BCL中的BlockingCollection。如果您使用的是早期版本,则可以将其替换为Stephen Toub的BlockingQueue

注意:在您的情况下可能需要进一步重构。此代码尝试保留原始元素中的一些常用元素。

public class Example
{
    private BlockingCollection<Controller> m_Queue = new BlockingCollection<Controller>();

    public Example()
    {
        for (int i = 0; i < 4; i++)
        {
            var thread = new Thread(
                () =>
                {
                    while (true)
                    {
                        Controller controller = m_Queue.Take();
                        // Do whatever you need to with Contoller here.
                    }
                });
            thread.IsBackground = true;
            thread.Start();
        }
    }

    public void Exec()
    {
        try
        {
            AutoResetEvent autoEvent = new AutoResetEvent(false);
            int vQtd = Queue.GetAllMessages().Length
            while (vQtd > 0)
            {
                m_Queue.Add(new Controller(Queue.Receive(), autoEvent));
            }
        }
        catch (Exception ex)
        {
            ExceptionPolicy.HandleException(ex, "RECOVERABLE");
        }
    }
}

修改

或者更好,因为MessageQueue是线程安全的:

public class Example
{
    public Example()
    {
        for (int i = 0; i < 4; i++)
        {
            var thread = new Thread(
                () =>
                {
                    while (true)
                    {
                      if (/* between 9am and 6pm */)
                      {
                        Message message = queue.Receive();
                        Controller controller = new Controller(message, /* AutoResetEvent? */);
                        // Do whatever you need to with Contoller here.
                        // Is the AutoResetEvent really needed?
                      }
                    }
                });
            thread.IsBackground = true;
            thread.Start();
        }
    }
}

答案 1 :(得分:0)

您有两种选择。您可以在每个带有Thread.Sleep()的消息之后插入延迟,或者降低轮询线程的线程优先级。如果降低线程优先级,CPU使用率仍然很高,但不应该影响性能。

编辑:或者您可以将线程数从4减少到3以留下一个核心用于其他处理(假设您有四核)。这当然会降低您的出列吞吐量。

Edit2:或者,如果您运行的是.NET,则可以使用task parallel library重写整个思考。查找Parallel.ForEach()。如果您不熟悉线程,这应该可以避免一些步法。

答案 2 :(得分:0)

当所有线程都忙时,您显示的方法在紧密循环中运行。尝试这样的事情:

    while (vQtd > 0)
    {
        bool full = true;

        for (int y = 0; y < th.Length; y++)
        {
            if (this.th[y] == null || !this.th[y].IsAlive)
            {
                this.th[y] = new Thread(new ParameterizedThreadStart(ProcessMessage));
                this.th[y].Name = string.Format("Thread_{0}", y);
                this.th[y].Start(new Controller(queue.Receive(), autoEvent));
                vQtd--;
                full = false;
            }
        }

        if (full)
        {
            Thread.Sleep(500); // Or whatever it may take for a thread to become free.
        }
    }