黑马程序员--如何:对 Windows 窗体控件进行线程安全调用

 

在多线程学习过程中,碰到了未命名的错误,虽然TextBox.CheckForIllegalCrossThreadCalls = false;能关闭检测而跳过这个异常,但总感觉不对劲,既然微软弄出这个东西,肯定不是简单关闭检查那么简单的事。查了下MSDN,找到解决方法。

 

对 Windows 窗体控件进行线程安全调用
  1. 查询控件的 InvokeRequired 属性。

  2. 如果 InvokeRequired 返回 true,则使用实际调用控件的委托来调用 Invoke

  3. 如果 InvokeRequired 返回 false,则直接调用控件。

使用 BackgroundWorker 进行线程安全调用

在应用程序中实现多线程的首选方式是使用BackgroundWorker组件。BackgroundWorker 组件使用事件驱动模型实现多线程。后台线程运行 DoWork 事件处理程序,而创建控件的线程运行 ProgressChangedRunWorkerCompleted 事件处理程序。可以从 ProgressChangedRunWorkerCompleted 事件处理程序调用控件。

使用 BackgroundWorker 进行线程安全调用

  1. 创建一个方法,该方法用于执行您希望在后台线程中完成的工作。不要调用由此方法中的主线程创建的控件。

  2. 创建一个方法,用于在后台工作完成后报告结果。可以调用由此方法中的主线程创建的控件。

  3. 将步骤 1 中创建的方法绑定到 BackgroundWorker 的实例的 DoWork 事件,并将步骤 2 中创建的方法绑定到同一实例的 RunWorkerCompleted 事件。

  4. 若要启动后台线程,请调用 BackgroundWorker 实例的 RunWorkerAsync 方法。

下面的代码示例是一个完整的 Windows 窗体应用程序,它包含一个带有三个按钮和一个文本框的窗体。第一个按钮演示不安全的跨线程访问,第二个按钮演示使用 Invoke 实现的安全访问,而第三个按钮演示使用 BackgroundWorker 实现的安全访问。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace 线程练习
{
    public partial class FormThreadProcsafe : Form
    {
        delegate void SetTextCallback(string text);


        public FormThreadProcsafe()
        {
            InitializeComponent();
        }

        private void FormThreadProcsafe_Load(object sender, EventArgs e)
        {

        }

        /*不安全的跨线程访问*/
        private void setTextUnsafeBtn_Click(object sender, EventArgs e)
        {
            Thread demoThread = new Thread(new ThreadStart(ThreadProcUnsafe));

            demoThread.Start();

        }
        private void ThreadProcUnsafe()
        {
            this.textBox1.Text = "这是不安全的调用方法.";//抛出异常,demoThread不能直接访问不是自己创建的textBox1
        }

        /*使用 Invoke 实现的安全访问*/

private void setTextSafeBtn_Click(object sender, EventArgs e) { Thread demoThread = new Thread(new ThreadStart(ThreadProcSafe)); demoThread.Start(); } private void ThreadProcSafe() { this.SetText("这是使用Invoke实现安全的调用方法."); } private void SetText(string text) { //检查控件的InvokeRequired是否为true,是则ThreadProcSafe方法会创建SetTextCallback的一个实例,并将该实例传递给窗体的Invoke方法。 //这使得SetText方法被创建TextBox控件的线程调用,而且在此线程上下文中将直接设置Text属性 if (this.textBox1.InvokeRequired) { SetTextCallback d = new SetTextCallback(SetText); this.Invoke(d, new object[] { text }); } else//InvokeRequired为false则直接赋值 { this.textBox1.Text = text; } } /*使用 BackgroundWorker 实现的安全访问(还是不太明白)*/ private void setTextBackgroundWorkerBtn_Click(object sender, EventArgs e) { this.backgroundWorker1.RunWorkerAsync(); } private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { this.textBox1.Text = "使用 BackgroundWorker 实现的安全访问."; } } }

运行界面如下:

 

在运行该应用程序并单击“不安全的跨线程访问”(不安全调用)按钮时,Visual Studio 调试器会指示出现异常。调试器在尝试直接对文本框写入内容的后台线程中的行上停止。在单击“使用 Invoke 实现的安全访问”(安全调用)按钮时,文本框中会显示“这是使用Invoke实现安全的调用方法.”,这指示已调用 Invoke 方法。在单击“使用 BackgroundWorker 实现的安全访问”(安全 BW 调用)按钮时,文本框中会显示“使用 BackgroundWorker 实现的安全访问.”,这指示已调用 BackgroundWorkerRunWorkerCompleted 事件的处理程序。

posted @ 2012-11-09 10:17  sixstar01  阅读(411)  评论(0编辑  收藏  举报