如何在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);
 }

每次数据传入时,需要进行一些过滤和处理:我在传入数据队列中挖掘以搜索自定义通信协议的包。如果找到了一个包,就完成了更多的工作。这给了我两个选项:

  1. 在事件处理程序中进行过滤/处理。导致原始数据呈现延迟和/或处理时间过长导致所有内容冻结。
  2. 在UI线程中执行过滤/处理。导致UI锁定。

我更愿意直接将原始数据传递给UI,并有一个单独的过滤和处理线程,以先进先出的方式处理数据。串行端口DataReceivedHandler将数据推入处理队列,另一个线程将持续处理队列,并将任何结果传递给UI以供显示(或者在队列为空时进入睡眠状态)。

我如何实现这个?这个系统。线程命名空间充满了听起来很酷的类,但我找不到任何似乎适用于我正在尝试做的事情:(

如何在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();
}