在做一个日志工具 GUI,主界面上有一个 richtext 控件输出日志信息,当日志疯狂输出时,主界面就卡死了,有什么的解决方案呢? 日志是启用一个线程,线程中 Process 调用 cmd,委托事件输出日志。日志追加方法如下。我是才看了 3 天的 C#,网上资料太少了点
public void AppendConsole(string log)
{
if (string.IsNullOrWhiteSpace(log)) return;
//在 UI 线程中执行
consolebox.BeginInvoke(new Action(() =>
{
consolebox.AppendText(log);
consolebox.AppendText(Environment.NewLine);//追加换行符
}));
}
1
tanranran 2020-03-09 13:03:55 +08:00 1
限流
|
2
blueboyggh 2020-03-09 13:41:46 +08:00
貌似需要用 backgroundworker 吧
|
3
mcdull619 2020-03-09 13:49:49 +08:00
别在 UI 线程中输出 , 创建子线程做这些操作 .
|
4
geelaw 2020-03-09 14:18:08 +08:00 1
首先,大量进行 AppendText 本来性能就不行,使用 #1 的思路,限制 append 的频率,一次 append 多条消息(先拼好再送去 AppendText )。另一个思路是使用性能更好的控件,例如这里完全没有体现为什么要用 RichText。
@mcdull619 #3 是强行背诵式回答问题,对 UI 的变化只能在 UI 线程上进行。 |
6
x537196 OP @blueboyggh 试过了,也是会卡
|
7
Daming 2020-03-09 15:27:07 +08:00
一个最简单的
Task.Factory.StartNew(() => { // 和 UI 无关的逻辑 this.Invoke(new Action(()=>{ //UI 逻辑操作 })); }).ContinueWith(t => { if (t.IsFaulted) { // 记录异常 } }); |
9
ysc3839 2020-03-09 15:32:35 +08:00 via Android
@geelaw > 对 UI 的变化只能在 UI 线程上进行。
这似乎是不一定的? Windows UI 的多线程限制好像不那么严格。 按照微软文档所说 https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendmessage If the specified window was created by a different thread, the system switches to that thread and calls the appropriate window procedure. 看上去 Windows 会自动切到目标线程执行,不需要自己处理的。 |
10
darktutu 2020-03-09 15:37:09 +08:00
最近搞过一次界面卡死的问题,你先降低更新的频率,不要那么频繁。
|
12
geelaw 2020-03-09 16:21:13 +08:00 1
@ysc3839 WinForm 默认情况下会对每个 UI 变化进行检查,如果当前线程不是建立该 UI 对象的线程则直接抛出异常。
你应该认为所有的 UI 对象都相当于一个 STA COM 对象,而 Windows 提供的 SendMessage 等 API 相当于是带有 marshalling 的,因此如果你尝试从另一个线程 SendMessage 到 UI 对象,就相当于你进行了正确的跨 apartment COM 调用。SetWindowText 最终也会变成 SendMessage,因此调用 Win32 API 会有正确的结果。 然而这样随意的编程方式很危险——因为 SendMessage 自己会进行消息处理,你的 WndProc 必须是 reentrant 才行,大多数人写出来的都不是。WinForm 的做法就是默认不允许跨线程操作,程序员需要显式表达线程切换——好习惯从最开始就要培养。 |
14
darktutu 2020-03-09 20:22:23 +08:00
private void button1_Click(object sender, EventArgs e)
{ var task = new Task(() => { var index = 1; while(true) { AppendConsole(index.ToString()); index++; System.Threading.Thread.Sleep(50); } }); task.Start(); } public void AppendConsole(string log) { if (string.IsNullOrWhiteSpace(log)) return; //在 UI 线程中执行 richTextBox1.BeginInvoke(new Action(() => { richTextBox1.AppendText(log); richTextBox1.AppendText(Environment.NewLine);//追加换行符 })); } 兄弟,我这么跑也不会卡死啊? |
17
neilq 2020-03-10 15:34:06 +08:00 1
存在缓存里,每隔多少秒输出到 ui,或者每满多少兆输出到 ui,或者多种策略结合着来
|