C# 跨线程访问控件

本文介绍了在C# WinForm应用中如何处理跨线程访问控件的问题。针对.NET框架的线程安全机制,讨论了两种解决方法:禁用非法跨线程检查和使用委托与Invoke方法。推荐使用委托和Invoke来避免异常,确保多线程环境下控件的正确更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在用CS结构开发系统时,我们常会使用到多线程来处理程序,其中一个就是使用跨线程访问控件或设置控件属性。如果这样,在调试时会提示出错,不能在其它线程中修改创建控件的线程的控件的值。这是因为.net 2.0以后加强了安全机制,不允许在winform中直接跨线程访问控件的属性。

 

我们先看一下传统的程序:

设计界面如下:在窗体中添加一个Label控件(用来显示时间)

 

源程序:

using System;

using System.Windows.Forms;

using System.Threading;

 

namespace KingChat

{

    public partial class SystemLogin : DevExpress.XtraEditors.XtraForm

    {

            public SystemLogin()

             {

                   InitializeComponent();

         }

        

              private void SystemLogin_Load(object sender, EventArgs e)

              {

         Thread newThread = new Thread(newThreadStart(threadFuntion));

                   newThread.IsBackground = true;

                   newThread.Start();

         }

        

private void threadFuntion()

              {

                   while (true)

                   {

                       this.label1.Text =DateTime.Now.ToString();

                       Thread.Sleep(1000);

                   }

        }

}

}

 

当调试上面的程序是会提示出错。 

原因是:.NET 原则上禁止跨线程访问控件,因为这样可能造成错误的发生。

 

那么我们有什么方法解决C#跨线程访问控件问题:

1)禁止编译器对跨线程访问作检查,可以实现访问,但是出不出错不敢保证;

2)推荐的解决方法是采用代理,用代理方法来间接操作不是同一线程创建的控件。

 

解决方案一:

利用Control.CheckForIllegalCrossThreadCalls = false;加入这句代码调试时也不会提示出错,可以正常执行下面的程序。这句代码就是说在这个类中我们不检查跨线程的调用是否合法(如果没有加这句话运行也没有异常,那么说明系统以及默认的采用了不检查的方式)。然而,这种方法不可取。我们查看CheckForIllegalCrossThreadCalls这个属性的定义,就会发现它是一个static的,也就是说无论我们在项目的什么地方修改了这个值,他就会在全局起作用。而且像这种跨线程访问是否存在异常,我们通常都会去检查。如果项目中其他人修改了这个属性,那么我们的方案就失败了,我们要采取另外的方案。

 

具体做法如下:

在窗体的Load方法中加入代码,其他不变

private void SystemLogin_Load(object sender, EventArgs e)

    {

         Control.CheckForIllegalCrossThreadCalls =false;

         Thread newThread = new Thread(newThreadStart(threadFuntion));

         newThread.IsBackground = true;

         newThread.Start();

}

 

 

解决方案二:

就是使用delegateinvoke来从其他线程中控制控件信息。

源代码如下:

using System;

using System.Windows.Forms;

using System.Threading;

 

namespace KingChat

{

    public partial class SystemLogin : DevExpress.XtraEditors.XtraForm

{

    private delegate void DelegateFunction();//代理

            public SystemLogin()

             {

                   InitializeComponent();

         }

        

              private void SystemLogin_Load(object sender, EventArgs e)

              {

                   Thread thread =new Thread(CrossThreadFlush);

                   thread.IsBackground = true;

                   thread.Start();

         }

        

private void CrossThreadFlush ()

              {

                   while (true)

                   {

                       Thread.Sleep(1000);

                       ThreadFunction();

                   }

        }

        

         private void ThreadFunction()

{

     if (this.label1.InvokeRequired)//等待异步

                           {

                       DelegateFunction df =new DelegateFunction(ThreadFunction);

                       this.Invoke(df);//通过代理调用刷新方法

                   }

                   else

                   {

                       this.label1.Text =DateTime.Now.ToString();

                   }

}

}

}

      

我们可以看到问题已经被解决了,通过等待异步,我们就不会总是持有主线程的控制,这样就可以在不发生跨线程调用异常的情况下完成多线程对winform多线程控件的控制了。

 

刚运行可执行文件时会先显示Label控件的Text值,大概一秒后就可以看到时间:

 

4                           5

 

解决方案二的优化:

利用了delegate的异步调用。

using System;

using System.Windows.Forms;

using System.Threading;

 

namespace KingChat

{

    public partial class SystemLogin : DevExpress.XtraEditors.XtraForm

{

    private delegate void DelegateFunction();//代理

            public SystemLogin()

             {

                   InitializeComponent();

         }

        

              private void SystemLogin_Load(object sender, EventArgs e)

              {

                   Thread thread =new Thread(CrossThreadFlush);

                   thread.IsBackground = true;

                   thread.Start();

         }

        

private void CrossThreadFlush ()

              {

                   DelegateFunction df =new DelegateFunction(ThreadFunction);

                   df.BeginInvoke(null,null);

        }

        

         private void ThreadFunction()

{

     while (true)

       {

                       this.label1.Text =DateTime.Now.ToString();

                       Thread.Sleep(1000);

                   }

}

}

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值