因此,我正在从arduino中读取恒定的串行数据流,以验证程序中的某些内容。但是显示这些会锁定UI线程。因此,我的“解决方案”是创建一个保留串行数据的缓冲区,然后使用计时器以固定间隔而不是恒定流的形式将数据放置在UI线程上。
我的代码:
public partial class ConsoleWindow : Window
{
private SerialPort _serialPort;
private List<string> bufferStrings = new List<string>();
private readonly DispatcherTimer timer = new DispatcherTimer();
public ConsoleWindow(ref SerialPort serialPort)
{
InitializeComponent();
if (serialPort != null)
{
timer.Interval = new TimeSpan(0,0,0,0,80);
timer.Tick += PopQueue;
_serialPort = serialPort;
_serialPort.DataReceived += DataReceived;
timer.Start();
}
}
private void PopQueue(object sender, EventArgs e)
{
var queue = bufferStrings;
foreach (var queueString in queue)
{
AppendText(queueString);
}
bufferStrings.Clear();
}
private void DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (_serialPort != null)
{
bufferStrings.Add(((SerialPort)sender).ReadLine());
//AppendText(((SerialPort) sender).ReadLine());
}
}
public void AppendText(string text)
{
Application.Current.Dispatcher.Invoke(() =>
{
if (Output.Inlines.Count > 100)
{
Output.Inlines.Remove(Output.Inlines.FirstInline);
}
Output.Inlines.Add(text);
ScrollViewer.ScrollToBottom();
});
}
}
与此相关的问题是我得到了一个例外:System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute.'
。我知道为什么会发生,但是我不知道如何正确地做到这一点。而且也不知道该用什么Google。
答案 0 :(得分:4)
您可以采取以下两种方法来防止InvalidOperationException
:
var queue = bufferStrings.ToList();
来实现。请注意,您必须包括using System.Linq;
才能使用ToList()
。使用lock
关键字将迭代线程包围起来,以确保迭代线程安全:
private void PopQueue(object sender, EventArgs e)
{
lock(bufferStrings)
{
foreach (var queueString in bufferStrings)
{
AppendText(queueString);
}
bufferStrings.Clear();
}
}
private void DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (_serialPort != null)
{
lock(bufferStrings)
{
bufferStrings.Add(((SerialPort)sender).ReadLine());
//AppendText(((SerialPort) sender).ReadLine());
}
}
}
答案 1 :(得分:1)
最简单的解决方案是通过bufferStrings
结构使用Monitor
来同步对lock
队列的访问:
private void PopQueue(object sender, EventArgs e)
{
lock (bufferStrings)
{
foreach (var queueString in bufferStrings)
{
AppendText(queueString);
}
bufferStrings.Clear();
}
}
private void DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (_serialPort != null)
{
lock (bufferStrings)
{
bufferStrings.Add(((SerialPort)sender).ReadLine());
//AppendText(((SerialPort) sender).ReadLine());
}
}
}
答案 2 :(得分:1)
问题在于,当您使用IEnumerable
遍历foreach
时,该集合正在另一个线程中更改。
您需要的是可以同时添加和读取的集合。
在文件顶部添加
using System.Collections.Concurrent;
更改此:
private List<string> bufferStrings = new List<string>();
到
private ConcurrentQueue<string> bufferStrings = new ConcurrentQueue<string>();
更改
bufferStrings.Add(((SerialPort)sender).ReadLine());
到
bufferStrings.Enqueue(((SerialPort)sender).ReadLine());
然后,您可以从队列中读取内容而不必担心是否有其他内容正在写入:
private void PopQueue(object sender, EventArgs e)
{
while (bufferStrings.TryDequeue(out string dequeued))
AppendText(dequeued);
}
这只是一直试图将项目从队列中取出,直到没有更多为止。当队列为空时,TryDequeue
返回false。如果在此方法运行时继续添加项目,它将继续处理它们。
答案 3 :(得分:0)
响应式扩展提供了大多数现成的功能。