四联光电智能照明论坛

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 2517|回复: 0
打印 上一主题 下一主题

c#中如何跨线程调用windows窗体控件?

[复制链接]
  • TA的每日心情
    开心
    2022-6-10 09:59
  • 366

    主题

    741

    帖子

    9649

    积分

    超级版主

    Rank: 8Rank: 8

    积分
    9649
    跳转到指定楼层
    楼主
    发表于 2016-11-4 10:49:36 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

    我们在做winform应用的时候,大部分情况下都会碰到使用多线程控制界面上控件信息的问题。然而我们并不能用传统方法来做这个问题,下面我将详细的介绍。
    首先来看传统方法:


    1.       public partial class Form1 : Form
    2.     {
    3.         public Form1()
    4.         {
    5.             InitializeComponent();
    6.         }
    7.         private void Form1_Load(object sender, EventArgs e)
    8.         {
    9.             Thread thread = new Thread(ThreadFuntion);
    10.             thread.IsBackground = true;
    11.             thread.Start();
    12.         }
    13.         private void ThreadFuntion()
    14.         {
    15.             while (true)
    16.             {
    17.                 this.textBox1.Text = DateTime.Now.ToString();
    18.                 Thread.Sleep(1000);
    19.             }
    20.         }
    21.     }
    复制代码

    运行这段代码,我们会看到系统抛出一个异常:

    Cross-thread operation not valid:Control 'textBox1' accessed from a thread other than the thread it was created on .
    这是因为.net 2.0以后加强了安全机制,不允许在winform中直接跨线程访问控件的属性。那么怎么解决这个问题呢,下面提供几种方案。
    第一种方案,我们在Form1_Load()方法中加一句代码:


    1.       private void Form1_Load(object sender, EventArgs e)
    2.       {
    3.             Control.CheckForIllegalCrossThreadCalls = false;
    4.             Thread thread = new Thread(ThreadFuntion);
    5.             thread.IsBackground = true;
    6.             thread.Start();
    7.         }
    复制代码

          加入这句代码以后发现程序可以正常运行了。这句代码就是说在这个类中我们不检查跨线程的调用是否合法(如果没有加这句话运行也没有异常,那么说明系统以及默认的采用了不检查的方式)。然而,这种方法不可取。我们查看CheckForIllegalCrossThreadCalls 这个属性的定义,就会发现它是一个static的,也就是说无论我们在项目的什么地方修改了这个值,他就会在全局起作用。而且像这种跨线程访问是否存在异常,我们通常都会去检查。如果项目中其他人修改了这个属性,那么我们的方案就失败了,我们要采取另外的方案。
          下面来看第二种方案,就是使用delegate和invoke来从其他线程中控制控件信息。网上有很多人写了这种控制方式,然而我看了很多这种帖子,表明上看来是没有什么问题的,但是实际上并没有解决这个问题,首先来看网络上的那种不完善的方式:


    1.     public partial class Form1 : Form
    2.     {
    3.         private delegate void FlushClient();//代理
    4.         public Form1()
    5.         {
    6.             InitializeComponent();
    7.         }
    8.         private void Form1_Load(object sender, EventArgs e)
    9.         {
    10.             Thread thread = new Thread(CrossThreadFlush);
    11.             thread.IsBackground=true;
    12.             thread.Start();
    13.         }
    14.         private void CrossThreadFlush()
    15.         {
    16.             //将代理绑定到方法
    17.             FlushClient fc = new FlushClient(ThreadFuntion);
    18.             this.BeginInvoke(fc);//调用代理
    19.         }
    20.         private void ThreadFuntion()
    21.         {
    22.             while (true)
    23.             {
    24.                 this.textBox1.Text = DateTime.Now.ToString();
    25.                 Thread.Sleep(1000);
    26.             }
    27.         }
    28.     }
    复制代码


    使用这种方式我们可以看到跨线程访问的异常没有了。但是新问题出现了,界面没有响应了。为什么会出现这个问题,我们只是让新开的线程无限循环刷新,理论上应该不会对主线程产生影响的。其实不然,这种方式其实相当于把这个新开的线程“注入”到了主控制线程中,它取得了主线程的控制。只要这个线程不返回,那么主线程将永远都无法响应。就算新开的线程中不使用无限循环,使可以返回了。这种方式的使用多线程也失去了它本来的意义。
    现在来让我们看看推荐的解决方案:


    1. using System;
    2. using System.Collections.Generic;
    3. using System.ComponentModel;
    4. using System.Data;
    5. using System.Drawing;
    6. using System.Linq;
    7. using System.Text;
    8. using System.Windows.Forms;
    9. using System.Threading;

    10. namespace WindowsFormsApplication4
    11. {
    12.     public partial class Form1 : Form
    13.     {
    14.         private delegate void FlushClient(); //代理
    15.         Thread thread = null;
    16.         int counter = 0;
    17.         public Form1()
    18.         {
    19.             InitializeComponent();
    20.         }
    21.         private void button1_Click(object sender, EventArgs e)
    22.         {
    23.             this.listBox1.Items.Clear();
    24.             button1.Enabled = false;
    25.             thread = new Thread(CrossThreadFlush);
    26.             thread.IsBackground = true;
    27.             thread.Start();
    28.         }
    29.         private void button2_Click(object sender, EventArgs e)
    30.         {
    31.             thread.Suspend();
    32.             button1.Enabled = true;
    33.         }
    34.         private void CrossThreadFlush()
    35.         {
    36.             while (true)
    37.             {
    38.                 //将sleep和无限循环放在等待异步的外面
    39.                 Thread.Sleep(1000);
    40.                 ThreadFunction();
    41.             }
    42.         }
    43.         private void ThreadFunction()
    44.         {
    45.             if (this.listBox1.InvokeRequired)//等待异步
    46.             {
    47.                 FlushClient fc = new FlushClient(ThreadFunction);
    48.                 this.Invoke(fc); //通过代理调用刷新方法
    49.             }
    50.             else
    51.             {
    52.                 counter += 1;
    53.                 this.label1.Text = counter.ToString();
    54.                 this.listBox1.Items.Add(System.DateTime.Now.ToString());
    55.             }
    56.         }
    57.     }
    58. }
    复制代码

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


    原文:http://www.cnblogs.com/zhaotiant ... /03/17/1414135.html
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|Archiver|手机版|小黑屋|Silian Lighting+ ( 蜀ICP备14004521号-1 )

    GMT+8, 2024-5-6 20:50 , Processed in 1.062500 second(s), 23 queries .

    Powered by Discuz! X3.2

    © 2001-2013 Comsenz Inc.

    快速回复 返回顶部 返回列表