【问题标题】:UI Thread gets stuck by using BlockingCollection rapidly?快速使用 BlockingCollection 导致 UI 线程卡住?
【发布时间】:2012-05-28 16:02:44
【问题描述】:

我即将编写一个从 TCP 端口获取流数据的应用程序,并对它们进行一些实时计算。到目前为止一切都很好,但是当生产者线程开始对数据块进行一些打包时,用户界面和选框进度条会卡住(对于不规则的短时间段)(参见代码)。

        void Produce()
    {
        try
        {
            while (true)
            {
                foreach (Chunk _chunk in bcPort)
                {
                    if (_ThreadCanceler.IsCancellationRequested) break;
                    Chunk chunk = bcPort.Take();
                    chunk.TimeTracker = new Stopwatch();  
                    chunk.SegmentId = iSegmentId;
                    if (chunk.Channel + 1 == iChannels) iSegmentId++; // last channel, raise segment id.                     
                    iPrevChannel = chunk.Channel;
         //         _ProcessAndJoin.EnqueueTask(chunk, _ThreadCanceler);                       
                    iChunksProduced++;
                    _LogWriter.WriteMessage("Task " + Task.CurrentId.ToString() + "(producer): ADDED_ Chunk[" + chunk.Channel + ":" + chunk.Vals.Count.ToString() + ":" + chunk.SegmentId + "] [" + iChunksProduced + "]. " + bcPort.Count + " for takeaway. Thread: " + Thread.CurrentThread.ManagedThreadId.ToString());
                }
                if (_ThreadCanceler.IsCancellationRequested) break;
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("ForkAndCrate.cs: Produce(): " + ex.ToString());
        }
    }

我做了很多测试,发现对 BlockingCollection bcPort 的访问似乎是问题所在。 bcPort 不断从另一个数据加法器线程获取块,该线程也应该不会影响 ui 线程。所以我不明白以下内容:

1.) 为什么当我使用不同的线程添加和 打包块?

2.) 当我使用 BC 存储数据时,为什么会发生这种情况?这些线程安全集合不就是为了这个特定目的吗?

顺便说一句:Windows 7 ResourceManager 在流式传输期间显示 100% 的 CPU 使用率,每个块包含大约 2000 个浮点值,其中 4 或 5 个每秒涌入。我也禁用了记录器,但没有效果。消费者和评估线程被禁用。

除了 ui 线程,只有一个名为“ReceiveAndSave”的线程,它从传入的浮点值中生成块(参见代码,方法“Add”)。线程“Producer”正在做一些进一步的打包并将消费者的块排入队列(停用)。

   public void Add(short iChannel, float fValue)
    {
        try
        {
            _Benchmark.UpdateRec();  // one value received, update benchmark:
            if (!cdBasin[iChannel].Enqueue(fValue))
            {
                Chunk chunk = new Chunk();
                chunk.Vals = cdBasin[iChannel].ToListDeep;
                chunk.Channel = iChannel;
                bcPort.Add(chunk);
                cdBasin.AddOrUpdate(iChannel, new BoundedQueue<float>(iSegmentSizePerChannel), (key, oldValue) => new BoundedQueue<float>(iSegmentSizePerChannel));
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString(), "Stop", MessageBoxButtons.OK, MessageBoxIcon.Stop);
        }
    }

生产者以 'myThreads[0] = new Thread(Produce); 开始myThreads[0].Name = "生产"; myThreads[0].Start();'

【问题讨论】:

  • 不清楚您发布的代码在哪里运行,或者您实际进行了哪些线程...请说明您的情况。
  • add 方法似乎有点奇怪,但是每个块必须恰好有 500 个 val,并且必须保持与它们的通道的连接,这很难保证有这么多数据涌入。除此之外,出于性能原因,我不想在字典或 bc 上使用 remove,因为它非常昂贵,所以我正在更新而不是删除和添加。但是add方法不是问题,运行起来非常流畅快速,只要producer不访问BlockingCollection 'bcPort'即可。

标签: .net multithreading c#-4.0 thread-safety blockingcollection


【解决方案1】:

真的不会卡住,你只是给它太多的工作要做。这使得它不再处理它的低优先级任务,响应输入和绘制通知。这确实使它看起来像被卡住了,你的用户肯定会这么想。

关键是只以人眼可以理解的速度更新 UI。除了每秒超过 25 次更新的模糊之外,它什么也看不见。如果您将其推送到每秒多达一千次更新,您将获得您描述的行为。所以收集结果并且在足够的时间到期之前不要调用/更新。而且通常将您显示的信息压缩成可消耗的块,以便为用户提供有意义的反馈,每秒显示数百个新项目的列表对任何人都没有用。

【讨论】:

  • 谢谢。日志文件只是对我有帮助,看看事情是否进展顺利。对于用户反馈,我有一个评估器,它仅每秒调用 gui 已处理块的基准。所以我的问题更像是一个外观问题,并且选取框进度条似乎不适合显示连续处理。在这种计算密集型情况下(除了我的基准),还有其他很好的方法可以向用户显示持续的进度吗?
  • 一切皆有可能,一个简单的计数器就可以了。您的 sn-p 太不透明,无法给出具体建议。
猜你喜欢
  • 1970-01-01
  • 2011-11-02
  • 2022-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-07
  • 2018-10-06
相关资源
最近更新 更多