zoukankan      html  css  js  c++  java
  • C#跨线程操作控件的最简单实现探究

    随着程序复杂度的提高,程序不可避免会出现多个线程,此时就很可能存在跨线程操作控件的问题。

    跨线程操作UI控件主要有三类方式:

    1、禁止系统的线程间操作检查。(此法不建议使用

    2、使用Invoke(同步)或者BeginInvoke(异步)。(使用委托实现,并用lambda表达式简化代码

    3、使用BackgroundWorker组件。(此法暂不介绍,详情可见文末的参考资料

    先看一个跨线程操作失败的例子:

    新建一个Winform窗口程序项目,拖一个button1和label1控件到Form1窗体上。启动程序以后试图通过点击button1改变label1的值,完整代码如下:

    复制代码
     1 using System;
     2 using System.Threading;
     3 using System.Windows.Forms;
     4 
     5 namespace Windows跨线程调用控件
     6 {
     7     public partial class Form1 : Form
     8     {
     9         public Form1()
    10         {
    11             InitializeComponent();
    12         }
    13 
    14         private void button1_Click(object sender, EventArgs e)
    15         {
    16             Thread thread1 = new Thread(new ParameterizedThreadStart(UpdateLabel));
    17             thread1.Start("label已更新");
    18         }
    19         
    20         private void UpdateLabel(object str)
    21         {
    22             label1.Text = str.ToString();
    23         }
    24     }
    25 }
    复制代码

    点击button1以后运行报错:

    解决方案:

    方法一:禁止系统的线程间操作检查。

    代码就一句话:Control.CheckForIllegalCrossThreadCalls = false;通常写在Form1类的构造方法Form1()中。如下所示:

    复制代码
    1 public Form1()
    2         {
    3             InitializeComponent();
    4 
    5             Control.CheckForIllegalCrossThreadCalls = false;         
    6         }
    复制代码

    但是,这种方法是很不可靠的,有时候还是会报错。

    方法二:使用Invoke(同步)或者BeginInvoke(异步)最精简的代码如下:

    复制代码
     1 using System;
     2 using System.Threading;
     3 using System.Windows.Forms;
     4 
     5 namespace Windows跨线程调用控件
     6 {
     7     public partial class Form1 : Form
     8     {
     9         public Form1()
    10         {
    11             InitializeComponent();
    12         }
    13 
    14         private void button1_Click(object sender, EventArgs e)
    15         {
    16             Thread thread1 = new Thread(UpdateLabel);//可以省略线程的委托类型ParameterizedThreadStart
    17             thread1.Start("label已更新");
    18         }
    19 
    20         private void UpdateLabel(object str)
    21         {
    22             if (label1.InvokeRequired)//不同线程为true,所以这里是true
    23             {               
    24                 BeginInvoke(new Action<string> (x => {label1.Text = x.ToString();}),str);    
    25             }
    26         }
    27     }
    28 }
    复制代码

    说明:Action是.NET预定义好的委托,可以简化委托的语法,如果不清楚它的用法,可以搜索“Action和Func的用法”。

    将上面的两种方法总结在同一段程序里面如下所示:(注意看代码中的注释)

    复制代码
     1 using System;
     2 using System.Threading;
     3 using System.Windows.Forms;
     4 
     5 namespace Windows跨线程调用控件
     6 {
     7     public partial class Form1 : Form
     8     {
     9         public Form1()
    10         {
    11             InitializeComponent();
    12 
    13             /************* 方法一 ************/
    14             //Control.CheckForIllegalCrossThreadCalls = false;
    15 
    16         }
    17 
    18         private void button1_Click(object sender, EventArgs e)
    19         {
    20             Thread thread1 = new Thread(new ParameterizedThreadStart(UpdateLabel));
    21             thread1.Start("label已更新");
    22         }
    23 
    24 
    25         //如果控件的 Handle(句柄) 是在与调用线程不同的线程上创建的(说明您必须通过 Invoke 方法对控件进行调用),则为 true;否则为 false。
    26         private void UpdateLabel(object str)
    27         {
    28             if (label1.InvokeRequired)//当是不同的线程在访问时为true,所以这里是true
    29             {
    30                 /************* 方法二 ************/
    31                 //Action<string> actionDelegate = (x) => { this.label1.Text = x.ToString(); };
    32 
    33                 ////如果已经创建控件的句柄,则除了 InvokeRequired 属性以外,控件上还有四个可以从【任何线程】上安全调用的方法,
    34                 ////它们是:Invoke、BeginInvoke、EndInvoke 和 CreateGraphics。 
    35                 ////在后台线程上创建控件的句柄之前调用 CreateGraphics 可能会导致非法的跨线程调用。 
    36                 ////对于所有其他方法调用,则应使用调用 (invoke) 方法之一封送对控件的线程的调用。
    37                 //this.label1.BeginInvoke(actionDelegate, str);
    38 
    39 
    40                 /************* 方法二(变式) ************/
    41                 //也可以直接用下面一句话来完成
    42                 //Control.BeginInvoke 方法有两个重载:BeginInvoke(Delegate)    ,BeginInvoke(Delegate, Object[]),下式用的是第二个重载
    43                 this.BeginInvoke(new Action<string>((x) => { label1.Text = x.ToString(); }), str);
    44 
    45                 //如果启动的多线程不需要带可变的参数,那更简单:
    46                 //label1.BeginInvoke(new Action(() => { label1.Text = "aaa"; }));         
    47             }
    48         }
    49     }
    50 }
    复制代码

    参考资料:

    http://www.cnblogs.com/TankXiao/p/3348292.html

    http://www.cnblogs.com/txw1958/archive/2012/08/21/csharp-crossthread-widget.html

  • 相关阅读:
    DataAnnotations
    使用BizTalk实现RosettaNet B2B So Easy
    biztalk rosettanet 自定义 pip code
    Debatching(Splitting) XML Message in Orchestration using DefaultPipeline
    Modifying namespace in XML document programmatically
    IIS各个版本中你需要知道的那些事儿
    关于IHttpModule的相关知识总结
    开发设计的一些思想总结
    《ASP.NET SignalR系列》第五课 在MVC中使用SignalR
    《ASP.NET SignalR系列》第四课 SignalR自托管(不用IIS)
  • 原文地址:https://www.cnblogs.com/asdyzh/p/9840227.html
Copyright © 2011-2022 走看看