SerialPort 上多线程的最佳方法是什么 [英] What is a best approach for multithreading on SerialPort

查看:19
本文介绍了SerialPort 上多线程的最佳方法是什么的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因为我是多线程应用程序的新手,所以在开始编写代码之前,我想从更有经验的人那里得到一些建议......

as I am new in multithreaded application I would like to have some advice from more experienced people before starting to write the code...

我需要在串口事件中对串口接收到的数据进行排队,以便进一步处理.

I need to queue data received on serial port in serial port event for further processing.

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

So I have the following event handler:

    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...这个我已经知道 :-)).

Within the StartDataProcessing I need to dequeue strings and update MANY UI controlls (using the InvokeRequired...this I already know :-)).

实现这一目标的最佳方法和无冲突(无死锁)方法是什么?

What is the best approach and colision free (without deadlock) approach to achieve this?

如何在更多线程中调用 StartDataProcessing 方法并安全地出列(TryDequeue)行队列,进行所有需要的计算并更新 UI 控件?

How to call StartDataProcessing method in more threads and safely dequeue (TryDequeue) the lines queue, make all needed computations and update UI controlls?

我必须声明通信速度非常快,而且我没有使用标准的 SerialPort 类.如果我只是将所有接收到的字符串写入控制台窗口而不进行进一步处理,它会很好地工作.

I have to appoint that the communication is very fast and that I am not using the standard SerialPort class. If I simply write all received strings without further processing to console window it works just well.

我在 .NET 4.5 中工作.

I am working in .NET 4.5.

谢谢你的建议...

更新的问题:好的,那么使用 TPL 从 datareceived 事件运行任务的最佳方法是什么?是否有必要创建另一个类(对象)来处理数据并使用回调来更新 UI,或者是否可以从事件中加载一些表单方法?如果有人能给我指示在数据接收事件中究竟要做什么,我会非常高兴.第一步做什么,因为研究所有可能的方法不是我有时间做的解决方案.我需要从一些特定的方式开始......有很多不同的可能的多线程方法,在阅读它们之后我仍然更加困惑,我不知道什么是最好的最快的解决方案......通常的线程)、BackgroundWorker、TPL、异步等待...?:-( 因为我的应用程序使用 .NET 4.5,所以我想使用一些最先进的解决方案 :-) 感谢您的任何建议...

Updated question: Ok, so what will be the best way to run the task from the datareceived event using TPL? Is it necessary to create another class (object) that will process data and use callbacks to update UI or it is possible to load some form method from the event? I'll could be very happy if someone can give me the direction what exactly to do within the datareceived event. What to do as the first step because studying all possible ways is not the solution I have time for. I need to begin with some particular way... There is so many different possible multithreading approaches and after reading about them I am still more confused and I don't know what will be the best a fastest solution... Usual Thread(s), BackgroundWorker, TPL, async-await...? :-( Because my application uses .NET 4.5 I would like to use some state-of-the-art solution :-) Thank you for any advice...

推荐答案

所以经过大量尝试后,现在我很满意.最后,我使用了标准的 .NET SerialPort 类,因为第三方 Serial 类会导致更高波特率 (115200) 的某些问题.它直接使用 WinAPI,因此最终代码是混合的 - 托管和非托管.现在,即使是标准的 .NET 4.5 SerialPort 类也能正常运行(我已经让我的应用程序成功运行了一整夜).

So after a lot of trying it is working to my satisfaction now. Finally I've used the standard .NET SerialPort class as the third-party Serial class causes somae problems with higher baudrates (115200). It uses WinAPI directly so the finall code was mixed - managed and unmanaged. Now, even the standard .NET 4.5 SerialPort class works well (I've let my application successfully running through a whole night).

因此,对于需要处理 C#SerialPort 和更高速率的每个人(仅用于说明 - 向 PC 发送消息的设备是 STM32F407/using USART 2/.我也用 Arduino Due 尝试过,它也能正常工作)我的 datareceived 事件现在采用以下形式:

So, for everyone that need to deal with C#, SerialPort and higher rates (only for clarification - the device sending messages to PC is the STM32F407 /using USART 2/. I've tried it also with Arduino Due and it works as well) my datareceived event is in the following form now:

    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,没有消息号等)

Within the StartDataProcessing function: 1. TryDequeue(lines, out str) 2. Use the ThreadPool.QueueUserWorkItem(lCallBack1, tmp); where tmp is needed part of the str (without EOF, without the message number etc.)

lCallBack1 = new WaitCallback(DisplayData);

DisplayData 函数内更新所有 UI 控件

Within the DisplayData function all the UI controls are updated

这种方法混合了线程池和 TPL 方式,但这不是问题,因为无论如何 TPL 在后台操作中都会使用线程池.

This approach mixes the ThreadPool and TPL ways but it is not a problem because the ThreadPool is used by TPL in background operation anyway.

我尝试过的另一种工作方法如下:

Another working method I've tried was the following:

ThreadPool.QueueUserWorkItem(lCallBack, lines); 

而不是:

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

这种方法运行良好,但我没有在夜间运行中对其进行测试.

This method was working well but I've not tested it in over night run.

根据我的主观感受,Task.... 方法更新了控件更流畅,但这只能是我的个人感觉:-)

By my subjective perception the Task.... method updated the controls more smoothly but it can be only my personal feeling :-)

所以,我希望这个答案能帮助我从论坛中了解到的人,许多人正在处理基于微控制器 <--> PC 的不可靠通信

So, I hope this answer will help someone as I know from forums that many people are dealing with with unreliable communication based on the micocontroller <--> PC

我的(令人惊讶的 :-) )结论是标准的 .NET SerialPort 即使在更高的波特率下也能够处理消息.如果您仍然遇到缓冲区溢出的问题,请尝试使用 SerialPort 缓冲区大小和 SerialPort 阈值.对我来说,设置 1024/500 是令人满意的(微控制器发送的消息的最大大小为 255 字节,因此 500 字节意味着在触发事件之前有 2 条消息在缓冲区中.)

My (surprising :-) ) conclusion is that the standard .NET SerialPort is able to handle messages even at higher baudrates. If you still run into troubles with buffer overrun then try to play with the SerialPort buffer size and SerialPort threshold. For me the settings 1024/500 are satisfactory (max size of the message send by microcontroller is 255 bytes so 500 bytes means that 2 messages are in buffer before the event is fired.)

您还可以从 datareceived 事件中删除所有 SetXXXX 调用,因为它们并不是真正需要的,并且它们会稍微减慢通信速度...

You can also remove all SetXXXX calls from the datareceived event as they are not really needed and they can slow down the communication a little...

我现在非常接近实时数据捕获,这正是我所需要的.

I am very close to real-time data capturing now and it is exactly what I've needed.

祝大家好运:-)

这篇关于SerialPort 上多线程的最佳方法是什么的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆