首页 > 解决方案 > 迭代不断修改的 List

问题描述

所以我正在从我的 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.'. 我知道它为什么会发生,但我不知道如何正确地做到这一点。也不知道要谷歌什么。

标签: c#wpflistuser-interface

解决方案


您可以采取以下两种解决方案来防止InvalidOperationException

  1. 在遍历其内容之前将缓冲区复制到一个新列表。var queue = bufferStrings.ToList();您可以通过调用注意来做到这一点,您必须包括using System.Linq;才能使用ToList().
  2. 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());
            }
        }
    }
    

推荐阅读