繁体   English   中英

多线程c#应用程序占用了很高的CPU使用率

[英]multi-thread c# application hitting high CPU usage

我正在开发将连接到x个硬件设备的应用程序。 我对ReaderLayer进行了设计,以便有一个专用线程,代码在该线程中运行以连接到单个设备并连续读取设备数据缓冲区。 阅读器层的代码如下:

        while (true)
        {

                // read buffer from the reader
                IList<IRampActiveTag> rampTagList = ConnectedReader.ReadBuffer();
                if (rampTagList != null && rampTagList.Any())
                {
                    // trigger read event handler
                    if (ReadMessage != null)
                        ReadMessage(this, new RampTagReadEventArg(rampTagList));
                }


        }

在阅读器层之上构建了一个逻辑层,该逻辑层负责处理从阅读器接收的数据并通过HTTP Post转发。 它有y个线程,每个线程运行一个单独的逻辑块,该逻辑块必须处理要写入其线程安全队列的相关信息。 逻辑层订阅Reader层公开的ReadForward事件,并通过写入其ConcurrentQueue将数据转发到相关逻辑块。

每个逻辑块中的代码都非常简单:

        public void ProcessLogicBuffer()
        {

            while (true)
            {   
                // Dequeue the list
                IRampActiveTag tag;
                LogicBuffer.TryDequeue(out tag);
                if (tag != null)
                {

                    ProcessNewTagReceivedLogic(tag);
                    Console.WriteLine("Process Buffer #Tag {0}. Buffer Count #{1}", tag.NewLoopId, LogicBuffer.Count);
                }

            }
        }

对于Reader层和Logic层while(true) ,循环的布局几乎相同。 但是,当我使用3个读取器和3个逻辑块进行测试时,我发现我的CPU利用率跃升到77%。 我迅速将CPU使用范围缩小到逻辑线程,因为我收到2个块的50%的使用率和1个块的25%的使用率。

我只需在3个块的逻辑线程中添加Thread.Sleep(100)就能将CPU使用率降低到3%左右,但是,我担心我的逻辑可能不同步。 通过查看样本,任何人都可以向我提出任何改进建议,因为生产代码将需要使用大约30个逻辑块。 我需要改变架构吗?

您在此循环中进行了很多不必要的轮询:

    public void ProcessLogicBuffer()
    {

        while (true)
        {   
            // Dequeue the list
            IRampActiveTag tag;
            LogicBuffer.TryDequeue(out tag);
            if (tag != null)
            {

                ProcessNewTagReceivedLogic(tag);
                Console.WriteLine("Process Buffer #Tag {0}. Buffer Count #{1}", tag.NewLoopId, LogicBuffer.Count);
            }

        }
    }

假设大多数时间队列是空的,那么此代码所做的大部分工作都是重复检查队列。 “我有东西吗?现在怎么样?现在呢?……”

您可以通过使用BlockingCollection替换ConcurrentQueue来摆脱所有无用的轮询。 假设您将LogicBuffer更改为BlockingCollection ,则循环如下所示:

    public void ProcessLogicBuffer()
    {
        foreach (var tag in LogicBuffer.GetConsumingEnumerable())
        {
            ProcessNewTagReceivedLogic(tag);
            Console.WriteLine("Process Buffer #Tag {0}. Buffer Count #{1}", tag.NewLoopId, LogicBuffer.Count);
        }
    }

GetConsumingEnumerable会在项目到达时使它们出队,并将继续这样做,直到集合为空并被标记为已添加完成。 请参见BlockingCollection.CompleteAdding 但是,真正的GetConsumingEnumerableGetConsumingEnumerable了不忙的等待。 如果该集合为空,则它等待项可用的通知。 它不会对TryDequeue进行很多无用的轮询。

更改使用ConcurrentQueue代码,使其改为使用BlockingCollection非常容易。 您大概可以在一个小时内完成。 这样做将使您的代码简单得多,并且将消除不必要的轮询。

更新

如果需要进行一些定期处理,则有两种选择。 如果您希望在读取BlockingCollection的同一循环中完成此操作,则可以将使用GetConsumingEnumerable的循环更改为:

Stopwatch sw = Stopwatch.StartNew();
TimeSpan lastProcessTime = TimeSpan.Zero;
while (true)
{
    IRampActiveTag tag;
    // wait up to 200 ms to dequeue an item.
    if (LogicBuffer.TryTake(out tag, 200))
    {
        // process here
    }
    // see if it's been 200 ms or more
    if ((sw.ElapsedMilliseconds - lastProcessTime.TotalMilliseconds) > 200)
    {
        // do periodic processing
        lastProcessTime = sw.Elapsed;
    }
}

这将为您提供200到400毫秒范围内的定期处理速率。 在我看来,这有点丑陋,可能无法满足您的目的。 您可以将超时从20毫秒减少到20毫秒,而不是200毫秒,这将使您更接近200毫秒的轮询速率,而对TryTake调用的代价是10倍。 您可能不会注意到CPU使用率的差异。

我的偏好是将定期处理循环与队列使用者分开。 创建一个计时器,每200毫秒触发一次,并使其工作。 例如:

public void ProcessLogicBuffer()
{
    var timer = new System.Threading.Timer(MyTimerProc, null, 200, 200);

    // queue processing stuff here

    // Just to make sure that the timer isn't garbage collected. . .
    GC.KeepAlive(timer);
}

private void MyTimerProc(object state)
{
    // do periodic processing here
}

这将使您的更新频率非常接近200毫秒。 计时器proc将在单独的线程上执行,为true,但是该线程处于活动状态的唯一时间将是在计时器触发时。

确实很简单,您永远不会屈服于控制,因此您的代码始终在执行,因此代码的每个线程都使用100%的1核(如果使用4核,则使用25%的cpu)。

那里没有魔术,Windows不会“试图猜测”您想要等待并限制您,您需要明确等待,您需要显示ProcessNewTagReceivedLogic的实现,但我猜测您正在“某个地方”网络连接,而不是轮询(只需检查一次,直到得到非null的值),而是要调用实际上持有并产生直到得到答案的东西。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM