快速将文本附加到文本框
我有一个 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
的调用上。
顺便说一句,我在 Java、C++ 和 C 方面拥有多年的经验。我使用 C# 已经是第 5 个月了。
I have a BackgroundWorker
thread that is posting messages, using BeginInvoke
on a textbox in the GUI. The method, write_debug_text
, that displays text in the textbox uses AppendText
and also writes the text to the Console
.
The appearance is that the BackgroundWorker
is writing too fast for the write_debug_text
to keep up. I set a breakpoint at write_debug_text
and have to wait a long time before it is hit. Many calls to 'BeginInvoke` occur before the breakpoint is hit.
I'm looking for a real-time display of messages on the UI, much like the System.Console
in the VS C# Express IDE.
From searching on SO, I understand that AppendText
is the faster method to use and that strings may have to be reallocated.
Some replies suggest using StringBuilder
then periodically writing that text to the textbox. But this requires adding more events and timers; which I would rather not do (my simple application keeps getting more and more complex).
How can I either write real-time to the Textbox (and have it display)?
My current idea is to create a widget inheriting from Textbox that uses a text queue and timer.
Edit 1: Sample code
Here is a fragment of my code:
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;
}
I have tried changing BeginInvoke
to Invoke
and my application is hung. When I pause / break using the debugger, the execution pointer is on the call to Invoke
.
BTW, I've got many years experience with Java, C++, and C. I am on my 5th month with C#.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我使用了 @Jeffre L. Whitledge 的建议并减少了字符串分配的数量。由于这是一个具有固定数量字符串的封闭应用程序,因此我缓存了这些字符串。这产生了我的程序执行速度明显加快的副作用。
我的问题之一仍然是 Windows 响应消息的速度缓慢。当进度条更新时可以看到这一点。从发送消息(例如附加文本)到执行消息,存在一定的延迟。
I used @Jeffre L. Whitledge's advice and reduce the number of string allocations. Since this is a closed application with a fixed number of strings, I cached the strings. This produced a side-effect of my program executing significantly faster.
One of my issues is still the slowness in Windows responding to messages. This can be seen when the progress bar is updated. There is a definite delay from when a message is sent (such as append text), and when it is performed.
如果显示大量消息,则问题可能是内存分配问题。
假设每条消息的长度为 30 个字符。这将是(大约)60 字节。我们进一步假设您每秒添加 10 条消息。然后,在第一秒生成的字符串将为:60 + 120 + 60 + 180 + 60 + 240 + 60 + 300 + ... + 60 + 600 = 3840 字节。
在第二秒,总数上升到 13,740 字节。
第三秒:29,640。
第四名:51,540。
...
第十名:308,940 字节。
18 秒后,达到 1 MB,显示的字符串每个为 11 kb。
在一分钟标记处,我们分配了 10 Mb 的字符串。
在两分钟内,这些消息占用了 43 Mb,并且每个字符串大小增加到 71 kb。
三分钟后,消息大小超过 100 kb,并且有近 100 Mb 专用于它们。
这就是为什么 StringBuilder 对于构建长字符串如此重要!
但由于您的计划是显示每个中间步骤,因此 StringBuilder 在这里无法为您提供帮助。此 GUI 要求您生成字符串的 metric-boat-load。
解决此问题的一种可能的解决方案是,在将数据插入到字符串的后面时,切掉字符串的前面。您仍然会为字符串分配大量内存,但各个字符串本身的大小是有限的,因此,运行时会更容易在内存中为它们找到一个位置,并且分配率将也走下去。
这应该会减少你的垃圾收集压力:
If there are a great many messages that are being displayed, then the problem is likely to be memory allocation issues.
Let's assume that each message is 30 characters long. This would be (roughly) 60 bytes. Let's further assume that you are adding 10 messages per second. Then, in the first second the generated strings will be: 60 + 120 + 60 + 180 + 60 + 240 + 60 + 300 + ... + 60 + 600 = 3840 bytes.
In the second second the total rises to 13,740 bytes.
In the third second: 29,640.
4th: 51,540.
...
10th: 308,940 bytes.
After 18 seconds, one megabyte is reached and the displayed strings are 11 kb each.
At the one minute mark we are at 10 Mb of string allocated.
At two minutes 43 Mb are devoted to these messages, and the string sizes are increasing through 71 kb each.
After three minutes the messages are over 100 kb in size, and nearly 100 Mb are devoted to them.
This is why StringBuilder is so important for building long strings!
But since your plan is to display each intermediate step, StringBuilder won't help you here. This GUI requires that you generate a metric-boat-load of strings.
One possible solution to this problem is to chop off the front of the strings as you are inserting data into the back. You will still be allocating a lot of memory to strings, but the individual strings themselves will be of bounded size, and, thus, it will be much easier for the runtime to find a place for them in memory, and the rate of allocation will go way down as well.
This should reduce your garbage-collection pressure:
也许尝试 http://sourceforge.net/projects/fastlogconsole - 具有出色的性能
Maybe try http://sourceforge.net/projects/fastlogconsole - has excellent performance
如果您希望在文本框中实时写入,为什么不使用同步
Invoke
而不是使用BeginInvoke
来排队稍后调用的函数?If you want realtime writes inside the textbox, why not using the synchronous
Invoke
instead ofBeginInvoke
which queues the function to be called later?不要使用
BeginInvoke
。请改用Invoke
。请参阅添加数千行时应用程序变得无响应。这不是完全相同的问题(他使用的是 DataGridView 而不是文本框),但这是同一类事情。您的BackgroundWorker
正在启动一大堆异步任务。你最好让它一次只做一个。Don't use
BeginInvoke
. UseInvoke
instead. See Application Becomes Nonresponsive While Adding Thousands of Rows. That's not exactly the same issue (he's using a DataGridView rather than a textbox), but it's the same kind of thing. YourBackgroundWorker
is starting up a whole bunch of asynchronous tasks. You're better off making it do one at a time.您可以尝试使用
RichTextBox
控件来代替,并阻止它刷新其 UI,除非经常刷新。有点像StringBuilder
建议,但更简单一些。请参阅此SO问题作为示例如何做到这一点,You could try using a
RichTextBox
control instead, and prevent it from refreshing its UI, except every so often. Kind of like theStringBuilder
suggestion, but a little simpler. See this SO question for an example of how to do that,