簡體   English   中英

SerialPort 上多線程的最佳方法是什么

[英]What is a best approach for multithreading on SerialPort

因為我是多線程應用程序的新手,所以在開始編寫代碼之前,我想從更有經驗的人那里得到一些建議......

我需要在串行端口事件中對串行端口接收到的數據進行排隊以進行進一步處理。

所以我有以下事件處理程序:

    void jmPort_ReceivedEvent(object source, SerialEventArgs e)
    {
        SetStatusLabel("Iddle...", lbStatus);
        SetPicVisibility(ledNotReceiving, true);
        SetPicVisibility(ledReceiving, false);

        String st = jmPort.ReadLine();
        if (st != null)
        {              
            lines.Enqueue(st); //"lines" is the ConcurrentQueue<string> object 
            StartDataProcessing(lines); //???
            SetStatusLabel("Receiving data...", lbStatus);
            SetPicVisibility(ledNotReceiving, false);
            SetPicVisibility(ledReceiving, true);
        }
        else
        {
            jmPort.Close();
            jmPort.Open();
        }
    } 

在 StartDataProcessing 中,我需要使字符串出列並更新許多 UI 控件(使用 InvokeRequired...這我已經知道了 :-))。

實現這一目標的最佳方法和無沖突(無死鎖)方法是什么?

如何在更多線程中調用 StartDataProcessing 方法並安全地出列(TryDequeue)行隊列,進行所有需要的計算並更新 UI 控件?

我必須指定通信速度非常快,而且我沒有使用標准的 SerialPort 類。 如果我只是將所有接收到的字符串寫入控制台窗口而無需進一步處理,則它運行良好。

我在 .NET 4.5 中工作。

謝謝你的任何建議...

更新的問題:好的,那么使用 TPL 從 datareceived 事件運行任務的最佳方法是什么? 是否有必要創建另一個類(對象)來處理數據並使用回調來更新 UI,或者是否可以從事件中加載一些表單方法? 如果有人能給我指示在數據接收事件中究竟要做什么,我會非常高興。 第一步做什么,因為研究所有可能的方法不是我有時間做的解決方案。 我需要從一些特定的方式開始......有很多不同的可能的多線程方法,在閱讀它們之后我仍然更加困惑,我不知道什么是最好的最快的解決方案......通常的線程)、BackgroundWorker、TPL、異步等待...? :-( 因為我的應用程序使用 .NET 4.5,所以我想使用一些最先進的解決方案 :-) 感謝您的任何建議...

因此,經過多次嘗試后,它現在使我感到滿意。 最后,我使用了標准的.NET SerialPort類,因為第三方 Serial 類會導致波特率較高的 somae 問題 (115200)。 它直接使用WinAPI ,因此最終代碼是混合的 - 托管和非托管。 現在,即使是標准的.NET 4.5 SerialPort類也能正常運行(我已經讓我的應用程序成功運行了一整夜)。

因此,對於需要處理C#SerialPort和更高速率的每個人(僅用於說明 - 向 PC 發送消息的設備是STM32F407 /使用 USART 2/。我也嘗試過使用Arduino Due並且它也能正常工作)我的 datareceived 事件現在采用以下形式:

    private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
    {
       //the SetXXXXX functions are using the .InvokeRequired approach 
       //because the UI components are updated from another thread than 
       //the thread they were created in

        SetStatusLabel("Iddle...", lbStatus);
        SetPicVisibility(Form1.frm.ledNotReceiving, true);
        SetPicVisibility(Form1.frm.ledReceiving, false);
        String st = serialPort1.ReadLine();
        if (st != null)
        {
            lines.Enqueue(st);
            Task.Factory.StartNew(() => StartDataProcessing(lines)); // lines is global ConcurrentQueue object so in fact there is no need to pass it as parameter
            SetStatusLabel("Receiving data...", lbStatus);
            SetPicVisibility(Form1.frm.ledNotReceiving, false);
            SetPicVisibility(Form1.frm.ledReceiving, true);
        }
    }

StartDataProcessing函數中: 1. TryDequeue(lines, out str) 2. 使用ThreadPool.QueueUserWorkItem(lCallBack1, tmp); 其中 tmp 是 str 的一部分(沒有EOF ,沒有消息號等)

lCallBack1 = new WaitCallback(DisplayData);

DisplayData函數中更新所有 UI 控件

這種方法混合了 ThreadPool 和 TPL 方式,但這不是問題,因為無論如何 TPL 在后台操作中都會使用 ThreadPool。

我嘗試過的另一種工作方法如下:

ThreadPool.QueueUserWorkItem(lCallBack, lines); 

代替 :

Task.Factory.StartNew(() => StartDataProcessing(lines));

這種方法運行良好,但我沒有在夜間運行中對其進行測試。

根據我的主觀感受,Task..

所以,我希望這個答案能幫助我從論壇中知道的人,許多人正在處理基於微控制器的不可靠通信<--> PC

我的(令人驚訝的 :-) )結論是標准的.NET SerialPort即使在更高的波特率下也能夠處理消息。 如果您仍然遇到緩沖區溢出的問題,請嘗試使用 SerialPort 緩沖區大小和 SerialPort 閾值。 對我來說,設置 1024/500 是令人滿意的(微控制器發送的消息的最大大小為 255 字節,因此 500 字節意味着在觸發事件之前緩沖區中有 2 條消息。)

您還可以從 datareceived 事件中刪除所有 SetXXXX 調用,因為它們並不是真正需要的,並且它們會稍微減慢通信速度...

我現在非常接近實時數據捕獲,這正是我所需要的。

祝大家好運:-)

在 StartDataProcessing 中,我需要使字符串出列並更新許多 UI 控件

你不可以。 您需要將字符串出列,然后再次將它們排入 UI 不同部分的多個隊列中。

如果你想快速,你可以將所有操作和 UI 分散到單獨的窗口中,這些窗口運行自己的單獨的消息泵,因此可以在單獨的 UI 線程中獨立更新。

一般流程是:

  • 1 個線程處理串行端口並獲取數據並將其排隊。
  • 另一個將它出列並將其分發到單獨的處理線程,從中
  • 數據進入多個輸出隊列,所有隊列都負責 UI 的一部分(取決於 UI 是否會變成瓶頸)。

出列不需要線程安全。 數據有多串行? 當同一塊的另一個更新到達時,您可以跳過數據嗎?

閱讀 TPL 和任務 - 有用於並行處理的基礎庫,其中包含大量文檔。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM