【问题标题】:Optimize an invoke logging function - multithreading优化一个调用日志功能——多线程
【发布时间】:2017-03-16 01:16:23
【问题描述】:

我的应用程序使用很多线程,每个线程都使用我的 AddDetailLog 函数将线程的进度添加到全局文本框中,因此我可以跟踪每个线程的进度。这个函数每秒可以调用超过 500 次,当我使用 VS2015 Profiler 的时候,我看到是这个函数占用了我 50% 的 CPU。当我加载超过 150 个线程时,我的 CPU 是 100%,所以我真的需要优化这个功能。

多线程优化功能:

public void AddDetailLog(string text)
    {
        if(Program.SHOWDETAILSLOG)
        {
            if (this.textBox_log.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(AddDetailLog);
                this.Invoke(d, new object[] { text });
            }
            else
                this.textBox_log.AppendText("[" + DateTime.Now.ToString("HH:mm:ss") + "] - " + text + Environment.NewLine);
        }
    }

由于多线程的问题,我需要检查我的 textBox 是否需要调用,因为所有线程(不是 backgroundWorker)都在我的全局表单上的同一个 textBox 上起作用。

如果有人有任何想法来优化这个功能,我会很高兴听到。

谢谢

【问题讨论】:

  • 您可以创建一个缓冲区来存储消息(不显示它们),然后有一个每 1 秒左右触发一次的计时器将其附加到 TextBox...。或者问问自己, “我真的需要查看所有这些日志消息吗?”

标签: c# multithreading optimization


【解决方案1】:

首先,Control.Invoke() 阻塞了调用进程,最终破坏了后台线程的目的,因为它们最终会通过 GDI 消息泵同步。您可以改用BeginInvoke() 来异步进行调用,从而让您的后台进程继续进行。

其次,以该速率写入TextBox 控件会减慢您的速度,因为对TextBox.Text 的任何更改都会触发TextChanged,其中会使用更新的文本重新绘制控件。每秒执行 500 次,尤其是使用任何自定义 TextChanged 处理程序(例如将文本框滚动到底部的代码)时,您会非常清晰地对 UI 线程进行 char-grilling。

相反,为什么不让您的记录器将条目写入一个更简单的内存对象,然后在您的表单上执行Timer 来检索、连接并在TextBox 控件中显示这些消息?考虑到高并发性,我推荐ConcurrentQueue<string>。您的后台工作人员执行“TryAdd”调用以将日志条目排入队列,然后您的 UI Timer.Tick 处理程序使用 TryTake 将它们出列并以对 UI 更友好的方式将它们附加到 TextBox(例如在附加到文本框)。

最后,TextBox 的内容越长,渲染所需的时间就越长,因为 TextBox 必须计算字符串的显示大小才能渲染滚动条并显示应该实际显示的字符串部分。我会在 30k 个字符左右后截断日志显示的“后端”。为了避免丢失潜在有价值的信息,同一个处理程序可以将消息传递给 StreamWriter 将所有内容写入文件;您可以在此处添加另一层异步处理以保持 UI 响应。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-16
    • 1970-01-01
    • 2019-04-06
    • 1970-01-01
    • 2016-12-07
    • 1970-01-01
    相关资源
    最近更新 更多