5

マイクロコントローラーを介してセンサーからいくつかのシリアルポートから読み取ろうとしています。各シリアル ポートは 2000 を超える測定値を受信します (各測定値は 7 バイトで、すべて 16 進数です)。そして、彼らは同時に発砲しています。現在、4 つのシリアル ポートからポーリングしています。また、各測定値を String に変換し、Stringbuilder に追加します。データの受信が完了すると、ファイルに出力されます。問題は、CPU 消費が非常に高く、80% から 100% の範囲にあることです。

私はいくつかの記事を読み、最後に Thread.Sleep(100) を置きました。データが来ないときの CPU 時間を短縮します。また、BytesToRead が 100 より小さい場合は、各ポーリングの最後に Thread.Sleep を配置します。これは、ある程度役立つだけです。

誰かがシリアルポートからポーリングして取得したデータを処理するソリューションを提案できますか? 何かを取得するたびに追加すると、問題が発生する可能性がありますか?

//I use separate threads for all sensors
private void SensorThread(SerialPort mySerialPort, int bytesPerMeasurement, TextBox textBox,     StringBuilder data)
    {
        textBox.BeginInvoke(new MethodInvoker(delegate() { textBox.Text = ""; }));

        int bytesRead;
        int t;
        Byte[] dataIn;

        while (mySerialPort.IsOpen)
        {
            try
            {
                if (mySerialPort.BytesToRead != 0)
                {
                  //trying to read a fix number of bytes
                    bytesRead = 0;
                    t = 0;
                    dataIn = new Byte[bytesPerMeasurement];
                    t = mySerialPort.Read(dataIn, 0, bytesPerMeasurement);
                    bytesRead += t;
                    while (bytesRead != bytesPerMeasurement)
                    {
                        t = mySerialPort.Read(dataIn, bytesRead, bytesPerMeasurement - bytesRead);
                        bytesRead += t;
                    }
                    //convert them into hex string
                    StringBuilder s = new StringBuilder();
                    foreach (Byte b in dataIn) { s.Append(b.ToString("X") + ","); }
                    var line = s.ToString();

                                            var lineString = string.Format("{0}  ----          {2}",
                                                      line,
                                                    mySerialPort.BytesToRead);
                    data.Append(lineString + "\r\n");//append a measurement to a huge Stringbuilder...Need a solution for this.

                    ////use delegate to change UI thread...
                    textBox.BeginInvoke(new MethodInvoker(delegate() { textBox.Text = line; }));

                    if (mySerialPort.BytesToRead <= 100) { Thread.Sleep(100); }
                }
            else{Thread.Sleep(100);}

            }
            catch (Exception ex)
            {
                //MessageBox.Show(ex.ToString());
            }
        }


    }
4

3 に答える 3

6

これは良い方法ではありません。DataReceived イベントで作業する方がはるかに優れています。

基本的にシリアル ポートでは、うまく機能する 3 段階のプロセスがあります。

  • シリアルポートからのデータ受信
  • 関連するデータのチャンクが得られるまで待機中
  • データの解釈

のようなもの

class DataCollector
{
    private readonly Action<List<byte>> _processMeasurement;
    private readonly string _port;
    private SerialPort _serialPort;
    private const int SizeOfMeasurement = 4;
    List<byte> Data = new List<byte>();

    public DataCollector(string port, Action<List<byte>> processMeasurement)
    {
        _processMeasurement = processMeasurement;
        _serialPort = new SerialPort(port);
        _serialPort.DataReceived +=SerialPortDataReceived;
    }

    private void SerialPortDataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        while(_serialPort.BytesToRead > 0)
        {
           var count = _serialPort.BytesToRead;
           var bytes = new byte[count];
           _serialPort.Read(bytes, 0, count);
           AddBytes(bytes);
        }
    }

    private void AddBytes(byte[] bytes)
    {
        Data.AddRange(bytes);
        while(Data.Count > SizeOfMeasurement)            
        {
            var measurementData = Data.GetRange(0, SizeOfMeasurement);
            Data.RemoveRange(0, SizeOfMeasurement);
            if (_processMeasurement != null) _processMeasurement(measurementData);
        }

    }
}

注: Add Bytes は、測定値としてカウントするのに十分な数になるまでデータを収集し続けます。または、データのバーストを取得した場合は、それを個別の測定値に分割します....そのため、一度は 1 バイト、次は 2 バイトを取得できます。次はもう 1 つ、それを測定値に変換します。マイクロがバーストで送信する場合、ほとんどの場合、1 つとして受信されますが、2 つに分割されることもあります。

それからあなたができるどこか

var collector = new DataCollector("COM1", ProcessMeasurement);

  private void ProcessMeasurement(List<byte> bytes)
            {
                // this will get called for every measurement, so then
                // put stuff into a text box.... or do whatever
            }
于 2013-02-27T23:06:51.957 に答える
2

まず、Using Stopwatches and Timers in .NET を読むことを検討してください。これにより、パフォーマンスの問題を分析し、コードのどの部分が問題を引き起こしているかを正確に知ることができます。

SerialPort.DataReceived イベントを使用して、データ受信プロセスをトリガーします。

受信プロセスとデータ操作プロセスを分離します。最初にデータを保存してから処理します。

読み取りループから UI を編集しないでください。

于 2013-02-27T23:34:00.960 に答える
1

あなたがすべきことは、受信データを処理するためのイベント ハンドラーを追加することだと思います。

mySerialPort.DataReceived += new SerialDataReceivedEventHandler(mySerialPort_DataReceived);

これにより、リッスンするシリアル ポートごとに個別のスレッドを実行する必要がなくなります。また、各 DataReceived ハンドラーは、利用可能なデータがあるときに正確に呼び出され、データの処理に必要なだけの CPU 時間を消費してから、アプリケーション/OS に譲ります。

それでも CPU 使用率の問題が解決しない場合は、処理が多すぎることを意味します。しかし、非常に高速なシリアル ポートがない限り、そこにあるコードが問題を引き起こすとは思えません。

于 2013-02-27T23:14:14.767 に答える