You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#多线程调用Invoke后窗体冻结问题求助

问题:多线程更新UI导致窗体冻结

我有一个仅包含多行文本框的C#窗体,需在窗体Load方法中创建3个工作线程,调用Contador类的contar方法。该方法循环10000次调用窗体类的函数,在文本框中打印线程编号与计数值。原本期望通过Invoke方法让UI操作在主线程执行、后台线程正常工作,但窗体出现冻结现象,附上代码寻求帮助。


主窗体类代码

namespace TestMultiHiloCSharp
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
      Contador contadorClassOne = new(1, this);
      Contador contadorClassTwo = new(2, this);
      // 注:原代码仅创建2个线程,需求为3个

      Task t1 = Task.Run(() => {
        contadorClassOne.contar();
      });

      Task t2 = Task.Run(() => {
        contadorClassTwo.contar();
      });
    }

    public void showMessage(String Mensaje)
    {
      this.Invoke(()=>
      {
        txtMultiLineTextBox.Text = Mensaje + "\r\n" + txtMultiLineTextBox.Text;
      });
    }
  }
}

Contador类代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestMultiHiloCSharp
{
  internal class Contador
  {
    int numHilo;
    Form1 thisForm;

    public Contador(int numHilo,Form1 thisForm)
    {
      this.numHilo = numHilo;
      this.thisForm = thisForm;
    }

    public void contar(){
      int cont = 0;
      while (cont < 100)
      {
        thisForm.showMessage("this is the theread: " + this.numHilo + " and the number: " + cont);
        cont++;
      }
    }
  }
}

问题原因

你使用的Invoke是同步跨线程调用:后台线程会等待主线程完成UI更新后,才会继续执行下一次循环。当循环次数较多(比如10000次)时,主线程会被大量同步请求占满,无法处理窗体的其他消息(比如鼠标点击、窗口重绘),最终导致窗体冻结。

解决方法

1. 改用异步调用BeginInvoke

把showMessage里的Invoke换成BeginInvoke,后台线程无需等待UI更新完成即可继续执行,不会阻塞主线程:

public void showMessage(String Mensaje)
{
    this.BeginInvoke(()=>
    {
        txtMultiLineTextBox.Text = Mensaje + "\r\n" + txtMultiLineTextBox.Text;
    });
}

2. 批量更新UI(针对10000次循环的优化)

如果循环次数是10000次,哪怕用BeginInvoke,10000次跨线程调用仍会给UI线程带来压力。可以在后台线程中缓存一批消息,再一次性更新到UI,减少跨线程调用次数:
修改Contador类的contar方法:

public void contar()
{
    int cont = 0;
    StringBuilder sb = new StringBuilder();
    while (cont < 10000)
    {
        sb.AppendLine($"线程{numHilo}:计数{cont}");
        cont++;
        // 每100次批量更新一次UI
        if (cont % 100 == 0)
        {
            thisForm.showMessage(sb.ToString());
            sb.Clear();
        }
    }
    // 处理最后一批未满100条的消息
    if (sb.Length > 0)
    {
        thisForm.showMessage(sb.ToString());
    }
}

3. 补充第三个线程(满足需求)

原代码只创建了2个线程,按照需求补充第三个:

private void Form1_Load(object sender, EventArgs e)
{
    Contador contadorClassOne = new(1, this);
    Contador contadorClassTwo = new(2, this);
    Contador contadorClassThree = new(3, this);

    Task.Run(() => contadorClassOne.contar());
    Task.Run(() => contadorClassTwo.contar());
    Task.Run(() => contadorClassThree.contar());
}

4. 解耦线程类与窗体(可选优化)

直接让Contador持有窗体引用可能导致内存泄漏,改用委托解耦:
修改Contador类:

internal class Contador
{
    int numHilo;
    private readonly Action<string> _updateUi;

    public Contador(int numHilo, Action<string> updateUi)
    {
        this.numHilo = numHilo;
        _updateUi = updateUi;
    }

    public void contar()
    {
        int cont = 0;
        StringBuilder sb = new StringBuilder();
        while (cont < 10000)
        {
            sb.AppendLine($"线程{numHilo}:计数{cont}");
            cont++;
            if (cont % 100 == 0)
            {
                _updateUi(sb.ToString());
                sb.Clear();
            }
        }
        if (sb.Length > 0)
        {
            _updateUi(sb.ToString());
        }
    }
}

窗体Load方法初始化:

private void Form1_Load(object sender, EventArgs e)
{
    var contador1 = new Contador(1, showMessage);
    var contador2 = new Contador(2, showMessage);
    var contador3 = new Contador(3, showMessage);

    Task.Run(() => contador1.contar());
    Task.Run(() => contador2.contar());
    Task.Run(() => contador3.contar());
}

内容的提问来源于stack exchange,提问作者Aldahir Ruiz Valdez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 03:01:12