【问题标题】:Collection is being changed in another thread before GUI thread is done with it在 GUI 线程完成之前,正在另一个线程中更改集合
【发布时间】:2013-08-08 21:46:33
【问题描述】:

我正在编写一个程序来记录称重传感器数据。数据收集在单独的线程中完成。存储数据的集合每 0.5 秒传回一次到 UI 中,并以图表形式显示。我正在使用 foreach 将其复制到 ZedGraph 的点列表中。问题是,在数据收集线程中的新数据更新集合之前,foreach 循环有时不会完成。这会导致抛出异常。

有人对我如何解决这个问题有任何建议吗?

预计到达时间:

private void record()
        {
            stopwatch.Reset();
            stopwatch.Start();

            comport.Open();
            comport.DiscardInBuffer();
            comport.DiscardOutBuffer();


            //comport.Write(COMMAND_COLDRESET + Environment.NewLine);
            //comport.Write(COMMAND_CONTINUOUSMODE + "<CR>");
            comport.Write(COMMAND_CONTINUOUSMODE + Environment.NewLine);

            recordingStartTrigger(); //** Fire Recording Started Event

            TimeOut.Start();
            updateTimer.Start();

            this.waitHandleTest.WaitOne(); //** wait for test to end

            TimeOut.Stop();
            updateTimer.Stop();

            comport.sendCommand(COMMAND_COMMANDMODE + Environment.NewLine);
            comport.Close();
            recordingStopTrigger(status); //** Fire Recording Stopped Event

            stopwatch.Stop();
        }


        //***********************************************************************************
        //** Events Handlers


        private void comDataReceived_Handler(object sender, SerialDataReceivedEventArgs e)
        {

            double force = 0;

            TimeOut.Stop();


            string temp = comport.getBuffer(true);
            if (!this.stop)
            {
                //force = Convert.ToDouble(temp);


                if(double.TryParse(temp, out force))
                {
                    report.Readings.Add(new Models.Reading { Time = stopwatch.ElapsedMilliseconds, Force = force });
                }
            }
            else
            {

                this.WaitEventTest.Set(); //** triggers the record method to continue and end the test.
            }


            TimeOut.Start(); //** reset TimeOut Timer
        }


    void updateTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {

        //** fire delagate that GUI will be listening to, to update graph.
        eventNewData(this, new eventArgsNewData(report));

    }

【问题讨论】:

  • 你能展示一些示例代码吗?
  • 您可以使用诸如持久不可变集合或写时复制等花哨的技巧来更好地做到这一点。你需要什么操作?
  • 可以使用可观察的并发集合来完成吗?当集合在另一个线程中更新时,UI 线程能否监听集合的变化?
  • 我不知道...我对可观察并发集合一无所知。

标签: c# .net multithreading


【解决方案1】:

解决此问题的最简单方法是停止将集合发送到 UI 线程。这从根本上说是不安全的,因为您在一个线程上读取它,而在另一个线程上写入。除非您使用专门为此任务设计的集合,否则它将失败

这里有一些可能的解决方案

  1. 使用新的并发集合之一,例如ConcurrentQueue&lt;T&gt;
  2. 将集合的副本传回 UI 线程

我的选择是#2。

【讨论】:

  • 我同意 Jared 的观点,将集合的副本返回给 UI,不要尝试遍历正在主动抽取数据的集合。
  • 是的,我在想也许通过副本会起作用,但我不确定。另外我如何传回副本?我会用我的一些代码更新我的问题。
  • @TheColonel26 如果您使用的是List&lt;T&gt;,那么只需传回new List&lt;T&gt;(theOriginalList)。并不是说这只会解决枚举问题。如果您还在后台线程上更改列表中的元素并从 UI 线程中读取它们,您将遇到此问题的另一个变体
  • 它是一个 ICollection,因为它是一个实体模型的一部分,在测试完成时会被添加到数据库中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-07
  • 1970-01-01
  • 2011-05-16
  • 2014-02-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多