yagebu1983
最近评论
文章分类
    收藏
      相册
      存档
      软件项目交易
      订阅我的博客
      XML聚合  FeedSky
      订阅到鲜果
      订阅到Google
      订阅到抓虾
      订阅到BlogLines
      订阅到Yahoo
      订阅到GouGou
      订阅到飞鸽
      订阅到Rojo
      订阅到newsgator
      订阅到netvibes

      转载 跨线程进行Windows窗体控件的访问收藏

      新一篇: c#多线程:线程池和异步编程 | 旧一篇: 多线程,重叠I/O,异步读写 命名通道服务器源码

      访问 Windows 窗体控件本质上不是线程安全的。如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态。还可能出现其他与线程相关的 bug,包括争用情况和死锁。确保以线程安全方式访问控件非常重要。
               .NET Framework 有助于在以非线程安全方式访问控件时检测到这一问题。在调试器中运行应用程序时,如果创建某控件的线程之外的其他线程试图调用该控件,则调试器会引发一个 InvalidOperationException,并提示消息:“从不是创建控件 control name 的线程访问它。”
               可以通过将 CheckForIllegalCrossThreadCalls 属性的值设置为 false 来禁用此异常。这会使控件以与在 Visual Studio 2003 下相同的方式运行。但是这样做会使程序出现预料之外的问题,如果程序的各线程之间没有互相争抢控件资源的情况,那么可以考虑采用这个办法。例如:
      public Form1()
               ...{
                   InitializeComponent();
                   CheckForIllegalCrossThreadCalls = false;
               }

      下面分别演示一下线程安全与非线程安全访问的实现
      这里演示了两种线程安全的控件调用方法:
      一种是利用delegate接口以及Invoke方法(黄色背景)
      一种是使用BackgroundWorker方法(绿色背景)
      目的都是通过调用创建该控件的线程,来对控件进行操作。
      namespace CrossThreadDemo
      ...{
           public class Form1 : Form
           ...{
               // 这个 delegate 用来实现对TextBox控件Text属性的异步操作
               delegate void SetTextCallback(string text);
               // 创建用来调用控件的线程对象
               private Thread demoThread = null;
               // 利用backgroundworker进行线程安全调用
               private BackgroundWorker backgroundWorker1;
               private TextBox textBox1;
               private Button setTextUnsafeBtn;
               private Button setTextSafeBtn;
               private Button setTextBackgroundWorkerBtn;
               private System.ComponentModel.IContainer components = null;
               // 按钮事件,创建非线程安全调用的线程
               private void setTextUnsafeBtn_Click(
                   object sender,
                   EventArgs e)
               ...{
                   this.demoThread =
                       new Thread(new ThreadStart(this.ThreadProcUnsafe));
                   this.demoThread.Start();
               }
               // 采用非线程安全调用
               private void ThreadProcUnsafe()
               ...{
                   this.textBox1.Text = "This text was set unsafely.";
               }
               // 按钮事件,创建线程安全调用的线程
               private void setTextSafeBtn_Click(
                   object sender,
                   EventArgs e)
               ...{
                   this.demoThread =
                       new Thread(new ThreadStart(this.ThreadProcSafe));
                   this.demoThread.Start();
               }
               // 调用worker线程,采用线程安全调用
               private void ThreadProcSafe()
               ...{
                   this.SetText("This text was set safely.");
               }
               // 使用线程安全方法对窗体控件进行操作
               // 首先查询控件的InvokeRequired属性,以此来判断是不是正在从创建这个控件的线程访问该控件
               // 如果不是正在从创建这个控件的线程访问该控件,该方法将创建 SetTextDelegate 的一个实例,
           // 并调用窗体的 Invoke 方法,这使得 SetText 方法被创建 TextBox 控件的线程调用,而且在此线程上下文中将直接设置 Text 属性
               //
               // 如果是从创建该控件的线程访问它,则直接对其进行操作
               private void SetText(string text)
               ...{
                   // InvokeRequired 比较线程ID以及创建控件的线程ID,不同则返回true
                   if (this.textBox1.InvokeRequired)
                   ...{    
                       SetTextCallback d = new SetTextCallback(SetText);
                       this.Invoke(d, new object[] ...{ text });
                   }
                   else
                   ...{
                       this.textBox1.Text = text;
                   }
               }
               // 该事件通过调用RunWorkerAsync方法启动BackgroundWorker
               // TextBox的Text属性将在 BackgroundWorker 发生 RunWorkerCompleted 事件之后设置好
               private void setTextBackgroundWorkerBtn_Click(
                   object sender,
                   EventArgs e)
               ...{
                   this.backgroundWorker1.RunWorkerAsync();
               }        
               // 通过调用创建控件的线程来更改控件属性,所以是线程安全的
               // BackgroundWorker 是首选的异步控件操作方法
               private void backgroundWorker1_RunWorkerCompleted(
                   object sender,
                   RunWorkerCompletedEventArgs e)
               ...{
                   this.textBox1.Text =
                       "This text was set safely by BackgroundWorker.";
               }
           }

      发表于 @ 2008年03月04日 18:28:00|评论(loading...)|编辑

      新一篇: c#多线程:线程池和异步编程 | 旧一篇: 多线程,重叠I/O,异步读写 命名通道服务器源码

      评论:没有评论。

      发表评论  


      当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
      Csdn Blog version 3.1a
      Copyright © yagebu1983