WinForm 跨线程更新界面

44 阅读4分钟

前言

我们在开发 C# 程序的时候,有时候需要在非UI主线程更新界面,为了避免跨线程更新UI的异常,下面进行详细介绍如何实现这一需求!

报错代码

下面的代码中的this.Text指的是一个winform的窗体,开启Task执行下面的代码以后直接报错,提示线程间操作无效,这是因为在WinForms应用程序中,UI元素(如控件)通常只能在创建它们的线程(通常是主线程或UI线程)上进行操作。

如果你尝试从另一个线程更新UI元素,将会引发跨线程操作异常(InvalidOperationException)。

为了安全地从非UI线程更新UI,你需要使用Invoke或BeginInvoke方法将更新操作封送回UI线程。

private void Test()
{
    for(int i=0;i<int.MaxValue;i++)
    {
        this.Text  = i.ToString();
        Thread.Sleep(1000);
    }
}
Task.Run(Test);

2、偷懒方法(不推荐)

在程序开始执行前设置下面的属性,意思就是不检查非UI线程访问UI,这样设置以后程序也不会报错,但是从众多查阅的资料可以得出该方法并不可靠,在多个线程同时并发访问控件时,可能会导致死锁,数据不一致等异常情况,所以并不推荐使用。

CheckForIllegalCrossThreadCalls = false;

3、正确方法(推荐)

3.1 control.Invoke(一般都是采用这种方式)

下面的代码中this代表当前form窗体,Invoke方法里面传入的是一个委托,这里Action就是一个委托,Action委托绑定的方法通过lamada表达式实现,lamada表达式绑定的内容就是更新 this.Text的值。 control.Invoke的作用就是在创建控件的基础句柄所在线程上执行委托,也就是control.Invoke里面的委托更新ui的操作会被切换到ui线程执行,虽然调用 control.Invoke的代码不在ui线程,而且必须是ui线程的代码执行完了以后,才会重新切换到调用线程。

private void Test()
{
    this.Invoke(new Action(() =>
    {
        this.Text = "100";
        Console.WriteLine("委托执行完成");
        Thread.Sleep(1000);
    }));

    Console.WriteLine("Test方法执行完成");
}
Task.Run(Test);

输出:

//委托执行完成
Test方法执行完成

从上面的输出可以看出 this.Invoke绑定的委托在ui线程执行完成以后,才返回到调用线程执行Console.WriteLine(“Test方法执行完成”);,这也称为同步调用。

3.2、control.BeginInvoke

下面的代码中this代表当前form窗体,BeginInvoke 方法里面传入的是一个委托,这里Action就是一个委托,Action委托绑定的方法通过lamada表达式实现,lamada表达式绑定的内容就是更新 this.Text的值。

control.BeginInvoke 的作用就是在创建控件的基础句柄所在线程上异步执行委托,也就是control.BeginInvoke里面的委托更新ui的操作会被切换到ui线程执行,同时BeginInvoke方法立即返回,接着执行调用线程后面的代码,而不是等到BeginInvoke绑定的委托执行完成以后才执行。

private void Test()
{
    this.BeginInvoke (new Action(() =>
    {
        this.Text = "100";
        Console.WriteLine("委托执行完成");
        Thread.Sleep(1000);
    }));
    Console.WriteLine("Test方法执行完成");
}
Task.Run(Test);

输出:

Test方法执行完成
委托执行完成

从上面的输出可以看出 this.BeginInvoke绑定的委托在ui线程执行的同时,也在调用线程执行Console.WriteLine(“Test方法执行完成”);,也就是说这两个线程并行执行,这也称为异步调用。

总结:对于winform跨线程更新界面推荐使用control.Invoke、 control.BeginInvoke,至于这两种方式分别在以下情况使用:

1、control.Invoke

对于程序执行速度要求不高的场合,推荐使用,这也是一般软件都使用的方式。

2、control.BeginInvoke

对于程序执行速度要求很高的场合,推荐使用,但是也有缺点,缺点就是具体界面在何时刷新我们是不知道的。

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。

也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!

优秀是一种习惯,欢迎大家留言学习!

作者:c#上位机

出处:blog.csdn.net/qq_34059233/article/details/144158424

声明:网络内容,仅供学习,尊重版权,侵权速删,歉意致谢!