因为我是多线程应用程序的新手,所以在开始编写代码之前,我想从更有经验的人那里得到一些建议......
我需要在串口事件中对串口上收到的数据进行排队,以便进一步处理。
所以我有以下事件处理程序:
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,或者可以从事件中加载一些表单方法?如果有人可以给我指明在datareceived事件中准确做什么的方向,我会非常高兴。做什么作为第一步因为研究所有可能的方法不是我有时间的解决方案。我需要从一些特定的方式开始......有许多不同的可能的多线程方法,在阅读它们后我仍然更加困惑,我不知道什么是最好的最快的解决方案...通常的线程(s),BackgroundWorker,TPL,async-await ......? :-(因为我的应用程序使用.NET 4.5我想使用一些最先进的解决方案:-)感谢您的任何建议...
答案 0 :(得分:2)
经过大量的努力,现在让我感到满意。
最后我使用了标准.NET SerialPort
类,因为第三方Serial类导致更高波特率的somae问题(115200)。它直接使用WinAPI
,因此finall代码是混合的 - 托管和非托管。现在,即使是标准的.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 ....方法更加顺畅地更新了控件,但这只是我个人的感受: - )
所以,我希望这个答案可以帮助我在论坛上知道很多人正在处理基于微控制器的不可靠通信&lt; - &gt; PC
我的(令人惊讶的:-))结论是标准.NET SerialPort
即使在更高的波特率下也能够处理消息。如果仍然遇到缓冲区溢出问题,请尝试使用SerialPort缓冲区大小和SerialPort阈值。对我来说,设置1024/500是令人满意的(微控制器发送的消息的最大大小为255字节,因此500字节意味着在事件被触发之前有2条消息在缓冲区中。)
您也可以从datareceived事件中删除所有SetXXXX调用,因为它们并不是真正需要的,它们可以减慢通信速度......
我现在非常接近实时数据捕获,这正是我所需要的。
祝大家好运: - )
答案 1 :(得分:0)
在StartDataProcessing中,我需要将字符串出列并更新许多UI控件
不,你没有。您需要将字符串出列,然后将它们再次排入队列的不同段的多个队列中。
如果你想要快速,你可以将所有操作和UI分散到单独的窗口中,这些窗口运行自己独立的消息泵,因此可以在单独的UI线程中独立更新。
一般过程是:
出队时不需要线程安全。数据的连续性如何?当同一件作品的另一次更新到来时,你可以跳过数据吗?
阅读TPL和任务 - 有并行处理的基础库,附带大量文档。