V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
x537196
V2EX  ›  Windows

#winform#子控件刷新时,主界面卡死,要如何解决

  •  
  •   x537196 · 2020-03-09 11:11:35 +08:00 · 2765 次点击
    这是一个创建于 1724 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在做一个日志工具 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);//追加换行符
                }));
    
            }
    
    17 条回复    2020-03-10 15:34:06 +08:00
    tanranran
        1
    tanranran  
       2020-03-09 13:03:55 +08:00   ❤️ 1
    限流
    blueboyggh
        2
    blueboyggh  
       2020-03-09 13:41:46 +08:00
    貌似需要用 backgroundworker 吧
    mcdull619
        3
    mcdull619  
       2020-03-09 13:49:49 +08:00
    别在 UI 线程中输出 , 创建子线程做这些操作 .
    geelaw
        4
    geelaw  
       2020-03-09 14:18:08 +08:00   ❤️ 1
    首先,大量进行 AppendText 本来性能就不行,使用 #1 的思路,限制 append 的频率,一次 append 多条消息(先拼好再送去 AppendText )。另一个思路是使用性能更好的控件,例如这里完全没有体现为什么要用 RichText。

    @mcdull619 #3 是强行背诵式回答问题,对 UI 的变化只能在 UI 线程上进行。
    x537196
        5
    x537196  
    OP
       2020-03-09 15:12:12 +08:00
    @geelaw 好的谢谢,如果不用这个控件, 选用什么控件好一点,因为我不太了解
    x537196
        6
    x537196  
    OP
       2020-03-09 15:12:42 +08:00
    @blueboyggh 试过了,也是会卡
    Daming
        7
    Daming  
       2020-03-09 15:27:07 +08:00
    一个最简单的

    Task.Factory.StartNew(() =>
    {
    // 和 UI 无关的逻辑

    this.Invoke(new Action(()=>{
    //UI 逻辑操作
    }));

    }).ContinueWith(t =>
    {
    if (t.IsFaulted)
    {
    // 记录异常
    }
    });
    x537196
        8
    x537196  
    OP
       2020-03-09 15:30:11 +08:00
    @Daming 不是任务的问题,应该是 UI 刷新太频繁了
    ysc3839
        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 会自动切到目标线程执行,不需要自己处理的。
    darktutu
        10
    darktutu  
       2020-03-09 15:37:09 +08:00
    最近搞过一次界面卡死的问题,你先降低更新的频率,不要那么频繁。
    x537196
        11
    x537196  
    OP
       2020-03-09 15:42:34 +08:00
    @darktutu 已经搞到 50ms 输出一次了
    geelaw
        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 的做法就是默认不允许跨线程操作,程序员需要显式表达线程切换——好习惯从最开始就要培养。
    geelaw
        13
    geelaw  
       2020-03-09 16:21:50 +08:00
    @x537196 #5 最简单的想法是用不 rich 的 TextBox。
    darktutu
        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);//追加换行符
    }));

    }

    兄弟,我这么跑也不会卡死啊?
    x537196
        15
    x537196  
    OP
       2020-03-10 11:59:30 +08:00
    @geelaw 用 richtextbox 是要用颜色标记一些信息出来
    x537196
        16
    x537196  
    OP
       2020-03-10 12:01:07 +08:00
    @darktutu 我开始设置的 sleep 是 2,然后还有一个内容变更事件没有处理
    neilq
        17
    neilq  
       2020-03-10 15:34:06 +08:00   ❤️ 1
    存在缓存里,每隔多少秒输出到 ui,或者每满多少兆输出到 ui,或者多种策略结合着来
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3235 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 00:17 · PVG 08:17 · LAX 16:17 · JFK 19:17
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.