C#winform主线程刷新UI时竟抛异常"从不是创建控件的线程访问它"
1 软件抛出示意图
2 使用场景说明
- 在Form2中添加了richTextBox1,用来临时刷新日志信息;
- 软件启动主窗体(Form1),并实例化了Form2;
- 有一条线程调用了Form2的实例化对象,刷新了richTextBox1信息
- 后续在(主线程)使用Form1打开(Show)Form2时,软件提示“System.InvalidOperationException:“线程间操作无效: 从不是创建控件“richTextBox1”的线程访问它。”
- 获取了调用时DisplayLog和Form2.Show()的线程号,发现刷新UI时,竟然不在主线程刷新,并且没有报错。
2.1 日志刷新模块
- 此时我是比较疑惑的,为什么主线程打开Form2时,还会报出这个异常?刷新控件信息时使用了主线程去刷新(如下图),怎么还会出现这个问题?
- 这不是拿着自己家的钥匙开自己家的门,邻居报警了“这个人是个贼,竟然想偷他自己家的东西!”
2.2 查看线程号
public void DisplayLog(string log)
{
if (this.InvokeRequired)
{
this.Invoke(new EventHandler(delegate { DisplayLog(log); }));
return;
}
try
{
LogUtil.Instance.Debug($"Form2.DisplayLog线程号:{Thread.CurrentThread.ManagedThreadId}");
richTextBox1.AppendText(log + Environment.NewLine);
richTextBox1.ScrollToCaret();
}
catch (Exception ex)
{
LogUtil.Instance.Error("", ex);
}
}
Time:2025-04-17 13:40:46,612
Thread ID:[1]
Log Level: DEBUG
Class:Log4net.Logger.LogUtil property: [(null)] -
Description:初始化Form2线程号:1
Time:2025-04-17 13:40:47,493
Thread ID:[4]
Log Level: DEBUG
Class:Log4net.Logger.LogUtil property: [(null)] -
Description:Form2.DisplayLog线程号:4
Time:2025-04-17 13:40:47,968
Thread ID:[1]
Log Level: DEBUG
Class:Log4net.Logger.LogUtil property: [(null)] -
Description:承载Form2线程号:1
- 果然线程号是有问题的,虽然我进行了两种处理方式。
2.2.1 添加变量法
随后,我定义了一个bool字段,在窗体的Load事件中将该值设置为true,在刷新日志的方法中判断是否该值的状态,若窗体未调用Load,接受到日志信息则不进行刷新。当Load加载了之后,再去刷新
- 测试结果,未抛异常
- 但是按照上面的方法去做,虽然解决了抛出异常的问题,但是,显而易见的是如果未打开Form2,那日志信息就不会记录,当打开Form2时,只能看到Load之后的日志信息,这样肯定是不能接受的。
- 所以另想他法。
2.2.2 构造函数加载资源方式
- 在构造函数中调用一次DisplayLog
public Form1()
{
InitializeComponent();
DisplayLog("模块启动成功");
}
public void DisplayLog(string log)
{
if (this.InvokeRequired)
{
this.Invoke(new EventHandler(delegate { DisplayLog(log); }));
return;
}
try
{
richTextBox1.AppendText(log + Environment.NewLine);
richTextBox1.ScrollToCaret();
}
catch (Exception ex)
{
LogUtil.Instance.Error("", ex);
}
}
这时测试的时候发现也没有报错,但是缺点是多了一条日志信息,这样我们是能接受。并且UI是在主线程刷新的。
Time:2025-04-17 13:42:43,633
Thread ID:[1]
Log Level: DEBUG
Class:Log4net.Logger.LogUtil property: [(null)] -
Description:初始化Form2线程号:1
Time:2025-04-17 13:42:44,203
Thread ID:[1]
Log Level: DEBUG
Class:Log4net.Logger.LogUtil property: [(null)] -
Description:Form2.DisplayLog线程号:1
Time:2025-04-17 13:42:45,674
Thread ID:[1]
Log Level: DEBUG
Class:Log4net.Logger.LogUtil property: [(null)] -
Description:承载Form2线程号:1
Time:2025-04-17 13:42:46,372
Thread ID:[1]
Log Level: DEBUG
Class:Log4net.Logger.LogUtil property: [(null)] -
Description:Form2.DisplayLog线程号:1
3 总结
无论是处理方式1还是处理方式2,仅解决了抛出异常的问题,但是并未搞清楚第一次界面使用的是子线程,为何软件竟然能够通过,并且主线程刷新UI竟然会异常。
> 希望有懂的大神,可以帮忙解惑下。