1

我有一个用 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,并有一个单独的过滤和处理线程以 FIFO 方式处理数据。串行端口 DataReceivedHandler 会将数据推送到处理队列中,另一个线程将继续处理队列并将任何结果传递给 UI 以进行呈现(或在队列为空时进入睡眠状态)。

我该如何实施?System.Threading 命名空间充满了听起来很酷的类,但我找不到任何似乎适用于我正在尝试做的事情:(

4

1 回答 1

1

我没有方便的 IDE,但您可以使用ConcurrentQueueTask.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();
}
于 2013-04-08T11:10:01.357 回答