4

我正在编写一个基准工具,它从线程中的本地服务器读取一堆变量。

            int countReads = 1000;

            Int64 count = 0;

            for (int i = 0; i < countReads; i++)
            {
                Thread.CurrentThread.Priority = ThreadPriority.Highest;
                DateTime start = DateTime.Now;

                session.Read(null, 0, TimestampsToReturn.Neither, idCollection, out ReadResults, out diagnosticInfos);

                DateTime stop = DateTime.Now;
                Thread.CurrentThread.Priority = ThreadPriority.Normal;

                TimeSpan delay = (stop - start);

                double s = delay.TotalMilliseconds;
                count += (Int64)s;

                Dispatcher.Invoke(DispatcherPriority.Render, new Action(() =>
                {
                    progressBar1.Value = i;
                }));
            }

            double avg = (double)count / countReads;

            Dispatcher.Invoke(DispatcherPriority.Input, new Action(() =>
            {
                listBox1.Items.Add(avg);
            }));

我正在计算进行读取所需的时间跨度并在最后获得平均时间跨度。

            DateTime start = DateTime.Now;

            session.Read(null, 0, TimestampsToReturn.Neither, idCollection, out ReadResults, out diagnosticInfos);

            DateTime stop = DateTime.Now

如果我在不更新进度条的情况下运行代码,则平均需要大约 5 毫秒。但如果我运行它

            Dispatcher.Invoke(DispatcherPriority.Render, new Action(() =>
            {
                progressBar1.Value = i;
            }));

平均大约需要 10 毫秒。

我的问题是,为什么使用进度条时时间跨度更高?我只是在计算读取的时间跨度。不包括进度条更新。

有什么办法可以疏散 ui-painting 以免影响我的阅读时间跨度?

谢谢你的帮助。

最好的祝福

4

4 回答 4

11

停止Invoke用于将进度信息传输到 UI 线程。将进度信息发布到共享数据结构或变量,并让 UI 线程在合理的时间间隔内使用计时器对其进行轮询。我知道似乎我们都被洗脑了,认为Invoke这是进行工作者到 UI 线程交互的万能方法,但对于简单的进度信息来说,它可能(而且通常是)最糟糕的方法。

在 UI 线程上使用计时器的轮询方法提供以下好处。

  • 它打破了Invoke强加于 UI 和工作线程的紧密耦合。
  • UI 线程可以决定何时以及多久更新一次进度信息,而不是相反。当你停下来思考它时,无论如何它应该是这样的。
  • 您可以在 UI 和工作线程上获得更多吞吐量。

我知道这并不能直接回答您关于为什么session.Read看起来运行速度较慢的问题。尝试将更新进度信息的策略从推模型(通过Invoke)更改为拉模型(通过计时器)。看看这是否有所作为。即使不是这样,由于上述原因,我仍然会坚持使用拉动模型。

于 2012-04-18T15:03:18.163 回答
2

这是 MSDN 关于Dispatcher.Invoke的说明

在 Dispatcher 关联的线程上同步执行指定的委托。

因此,基本上,Dispatcher.Invoke阻塞直到调度程序线程处理了请求。

试试Dispatcher.BeginInvoke吧。

于 2012-04-18T14:18:41.393 回答
0

如果当前正在执行的线程与您正在使用的 Dispatcher 相关联 -Invoke()将阻止该线程,因此在这种情况下尝试使用Dispatcher.BeginInvoke()它将异步完成工作。

MSDN,Dispatcher.Invoke 方法

Invoke 是同步操作;因此,直到回调返回后,控制才会返回给调用对象。

顺便说一句,只是有兴趣尝试一下DispatcherPriority.Send

于 2012-04-18T14:13:24.187 回答
0

我迟到了 9 年,但我认为这是一个更简单的解决方案:等到进度条值达到某个阈值再更新它。在我的示例中,我每隔五分之一的最大值刷新一次工具栏。

private static int progressBarMaxValue = -1;
private static int progressBarChunkSize = -1;

        public static void progressBarSetNotRealTimeValue(ProgressBar progressBar, int argNewValue)
        {
            if (progressBarMaxValue != -1)
            {
                if (argNewValue < progressBarChunkSize)
                {
                    //Threshold not reached yet, discard the new value.
                    return;
                }
                else
                {
                    //Allow the update, and set the next threshold higher.
                    progressBarChunkSize += progressBarChunkSize;
                }
            }

            if (Thread.CurrentThread.IsBackground)
            {
                progressBar.BeginInvoke(new Action(() =>
                {
                    if (progressBarMaxValue == -1)
                    {
                        progressBarMaxValue = progressBar.Maximum;
                        progressBarChunkSize = progressBar.Maximum / 5;
                    }

                    progressBar.Value = argNewValue;
                }));
            }
            else
            {
                progressBar.Value = argNewValue;
            }
        }
于 2021-08-13T16:49:39.837 回答