快速附加文本到文本框

时间:2011-03-30 18:45:56

标签: c# logging textbox real-time appendtext

我有一个BackgroundWorker帖子在GUI中的文本框上使用BeginInvoke发布消息。显示文本框中文本的方法write_debug_text使用AppendText,并将文本写入Console

外观是BackgroundWorker写得太快,write_debug_text无法跟上。我在write_debug_text处设置了一个断点,并且在它被击中之前必须等待很长时间。许多对'BeginInvoke`的调用发生在断点被触发之前。

我正在寻找UI上消息的实时显示,就像VS C#Express IDE中的System.Console一样。

从SO上搜索,我理解AppendText是更快的使用方法,并且可能必须重新分配字符串。

有些回复建议使用StringBuilder,然后定期将该文字写入文本框。但这需要增加更多的事件和计时器;我宁愿不这样做(我的简单应用程序越来越复杂)。

如何实时写入文本框(并显示)?

我目前的想法是创建一个继承自Textbox的小部件,它使用文本队列和计时器。

编辑1:示例代码

以下是我的代码片段:

    private m_textbox;
    //...
    m_textbox.BeginInvoke(new write_debug_text_callback(this.write_debug_text),
                                      new object[] { debug_text });
    return;

private void write_debug_text(string text)
{
    string time_stamp_text = "";
    time_stamp_text = DateTime.Now.ToString() + "   ";
    time_stamp_text += text;
    m_textbox.AppendText(time_stamp_text);
    m_textbox.Update();
    System.Console.Write(time_stamp_text);
    return;
}

我尝试将BeginInvoke更改为Invoke,我的应用程序已挂起。当我使用调试器暂停/中断时,执行指针正在调用Invoke

BTW,我有多年的Java,C ++和C经验。我在C#的第五个月。

6 个答案:

答案 0 :(得分:1)

如果正在显示大量消息,则问题可能是内存分配问题。

假设每封邮件的长度为30个字符。这将是(大约)60个字节。让我们进一步假设您每秒添加10条消息。然后,在第一秒中,生成的字符串将是:60 + 120 + 60 + 180 + 60 + 240 + 60 + 300 + ... + 60 + 600 = 3840字节。 在第二秒,总数上升到13,740字节。 在第三秒:29,640。 第4名:51,540。 ... 10日:308,940字节。

18秒后,达到1兆字节,显示的字符串各为11千字节。

在一分钟标记处,我们分配了10 Mb的字符串。 在两分钟时,43 Mb用于这些消息,字符串大小增加到每个71 kb。 三分钟后,消息的大小超过100 kb,并且将近100 Mb用于它们。

这就是StringBuilder对于构建长字符串非常重要的原因!

但由于您的计划是显示每个中间步骤,因此StringBuilder不会帮助您。此GUI要求您生成一个度量标准的字符串加载。

解决此问题的一种可能方法是在将数据插入背面时切断字符串的正面。您仍将为字符串分配大量内存,但单个字符串本身将具有有限大小,因此,运行时将更容易在内存中找到它们的位置,并且分配率将更高也走了。

这会减少你的垃圾收集压力:

private const int maxDisplayTextLength = 5000;

private void write_debug_text(string text)
{
    string time_stamp_text = "";
    time_stamp_text = DateTime.Now.ToString() + "   " + text;
    string previous = m_textbox.Text;
    if (previous.Length + time_stamp_text.Length > maxDisplayTextLength)
         m_textbox.Text = previous.Substring(0, maxDisplayTextLength - time_stamp_text.Length) + time_stamp_text;
    else
         m_textbox.Text = previous + time_stamp_text;
    m_textbox.Update();
    System.Console.Write(time_stamp_text);
    return;
} 

答案 1 :(得分:1)

答案 2 :(得分:0)

如果您希望实时在文本框内写入,为什么不使用同步Invoke而不是BeginInvoke,以便稍后调用该函数?

答案 3 :(得分:0)

请勿使用BeginInvoke。请改用Invoke。见Application Becomes Nonresponsive While Adding Thousands of Rows。这不是完全相同的问题(他使用DataGridView而不是文本框),但它是同样的事情。您的BackgroundWorker正在启动一大堆异步任务。你最好一次做一个。

答案 4 :(得分:0)

您可以尝试使用RichTextBox控件,并防止它刷新其UI,除非经常这样。有点像StringBuilder建议,但有点简单。有关如何执行此操作的示例,请参阅此SO question

答案 5 :(得分:0)

我使用了@Jeffre L. Whitledge的建议并减少了字符串分配的数量。由于这是一个具有固定数量字符串的封闭应用程序,因此我缓存了字符串。这产生了我的程序执行的副作用明显更快。

我的一个问题仍然是Windows响应邮件的速度缓慢。更新进度条时可以看到这一点。从发送消息(例如附加文本)到执行消息时有明确的延迟。