簡體   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