如何在UI线程外的串行端口数据上运行计算开销大的任务
本文关键字:运行 计算 开销 任务 数据 串行端口 UI 线程 | 更新日期: 2023-09-27 18:07:54
我有一个用c#编写的WPF应用程序,它提供了一个GUI,用于与串行端口上的一些设备通信。GUI包含一个原始输出字段,字符在输入时显示在其中,数据在其中过滤和处理。有时(当我的外部设备出现问题时)大量数据输入,程序间歇性地锁定。
下面是我的代码: private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
// get string out of buffer
// do filtering and processing (see details in text below)
UIdispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, callbackFunctionInUIThread, processedData);
}
每次数据传入时,需要进行一些过滤和处理:我在传入数据队列中挖掘以搜索自定义通信协议的包。如果找到了一个包,就完成了更多的工作。这给了我两个选项:
- 在事件处理程序中进行过滤/处理。导致原始数据呈现延迟和/或处理时间过长导致所有内容冻结。
- 在UI线程中执行过滤/处理。导致UI锁定。
我更愿意直接将原始数据传递给UI,并有一个单独的过滤和处理线程,以先进先出的方式处理数据。串行端口DataReceivedHandler将数据推入处理队列,另一个线程将持续处理队列,并将任何结果传递给UI以供显示(或者在队列为空时进入睡眠状态)。
我如何实现这个?这个系统。线程命名空间充满了听起来很酷的类,但我找不到任何似乎适用于我正在尝试做的事情:(
我没有IDE,但你可以使用ConcurrentQueue和Task.Factory.StartNew
像这样:
public class WorkItem //or use your SerialDataReceivedEventArgs
{
public int Id {get;set;}
public string SomeData {get;set;}
}
然后private ConcurrentQueue<WorkItem> WorkItems = new ConcurrentQueue<WorkItem>();
private CancellationTokenSource WorkTokenSource = new CancellationTokenSource();
private AutoResetEvent ResetEvent = new AutoResetEvent(true);
void Main()
{
StartProcessingWork();
for (var x = 0;x<1000;x++)
{
QueueWorkItem(new WorkItem(){Id= x,SomeData=String.Format("Item : {0}",x)});
}
Timer addItems = new Timer((a)=>
{
QueueWorkItem(new WorkItem(){Id= 0,SomeData=DateTime.Now.ToString()});
});
addItems.Change(1000,1000);
Timer cancel = new Timer((a)=>{
WorkTokenSource.Cancel();
ResetEvent.Set();
addItems.Change(Timeout.Infinite,Timeout.Infinite);
});
cancel.Change(5000,0);
}
public void StartProcessingWork()
{
Task.Factory.StartNew((o)=>
{
while(!WorkTokenSource.IsCancellationRequested)
{
WorkItem item = null;
if (WorkItems.TryDequeue(out item))
{
Console.WriteLine("Processing...");
ProcessWorkItem(item);
}
else
{
Console.WriteLine("Waiting...");
ResetEvent.WaitOne();
}
}
},WorkTokenSource.Token,TaskCreationOptions.LongRunning);
}
private void ProcessWorkItem(WorkItem item)
{
//Do some work here...
for (var x=0;x<100000;x++)
{
item.Id = x; //blah blah blah
}
//Use dispatcher to display
Dispatcher.CurrentDispatcher.BeginInvoke(()=>DisplayWorkItem(item));
}
private void DisplayWorkItem(WorkItem item)
{
//DO your display logic here...
}
public void QueueWorkItem(WorkItem item)
{
WorkItems.Enqueue(item);
ResetEvent.Set();
}