线程间操作无效: 从不是创建控件“...”的线程访问它。

来源:互联网 发布:php des加密解密 编辑:程序博客网 时间:2024/05/16 02:42

访问 Windows 窗体控件本质上不是线程安全的。如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态。还可能出现其他与线程相关的 bug,包括争用情况和死锁。确保以线程安全方式访问控件非常重要。

.NET Framework 有助于在以非线程安全方式访问控件时检测到这一问题。在调试器中运行应用程序时,如果创建某控件的线程之外的其他线程试图调用该控件,则调试器会引发一个 InvalidOperationException,并提示消息:“从不是创建控件 control name 的线程访问它。”

此异常在调试期间和运行时的某些情况下可靠地发生。强烈建议您在显示此错误信息时修复此问题。在调试以 .NET Framework 2.0 版之前的 .NET Framework 编写的应用程序时,可能会出现此异常。
注意
可以通过将 CheckForIllegalCrossThreadCalls 属性的值设置为 false 来禁用此异常。这会使控件以与在 Visual Studio 2003 下相同的方式运行。

对于这个问题的处理,如下所示:

例子的界面如下

 

 

 注意这里用到的这个backgroundworker控件。

 

下面的代码很直观的解释了解决此问题的方法:

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public delegate void setTextCallBack(string text);
        private Thread demoThread = null;

        public Form1()
        {
            InitializeComponent();
        }

        #region 非线程安全方式
        // 此事件句柄创建一个线程以非安全方式调用一个windows窗体控件,此处会报标题中提到的错误
        private void setTextUnsafeBtn_Click(object sender, EventArgs e)
        {
            this.demoThread = new Thread(new ThreadStart(this.ThreadProcUnsafe));
            this.demoThread.Start();
        }

        //此方法在工作者线程执行并且对TextBox控件作非安全调用
        private void ThreadProcUnsafe()
        {
            this.textBox1.Text = "This text was set unsafely.";
        }
        #endregion

 

        #region 线程安全方式
        // 此事件句柄创建一个以线程安全方式调用windows窗体控件的线程
        private void setTextSafeBtn_Click(object sender, EventArgs e)
        {
            this.demoThread = new Thread(new ThreadStart(this.ThreadProcSafe));
            this.demoThread.Start();
        }

        // 此方法在工作者线程执行并且对TextBox控件作线程安全调用
        private void ThreadProcSafe()
        {
            this.SetText("This text was set safely.");
        }

        // 如果调用线程和创建TextBox控件的线程不同,这个方法创建
        // 代理SetTextCallback并且自己通过Invoke方法异步调用它
        // 如果相同则直接设置Text属性
        private void SetText(string text)
        {
            if (this.textBox1.InvokeRequired)
            {
                setTextCallBack d = new setTextCallBack(SetText);
                this.Invoke(d, new object[] { text });
            }
            else
            {
                this.textBox1.Text = text;
            }
        }
        #endregion

 

        #region BackgroudWorker方式
        // BackgroundWorker是执行异步操作的首选方式
        // 此事件句柄通过调用RunWorkerAsync开启窗体的BackgroundWorker
        // 当BackgroundWorker引发RunworkerCompleted事件的时候TextBox
        // 控件的Text属性被设置
        private void setTextBackgroundWorkerBtn_Click(object sender, EventArgs e)
        {
            this.backgroundWorker1.RunWorkerAsync();
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.textBox1.Text = "This text was set safely by BackgroundWorker.";
        }
        #endregion
    }
}

参考了微软资料和网络。

 

无意中在网上遇到了总结更好的一篇文章,奇文共赏,感谢作者:

来自菩提树下的杨过http://www.cnblogs.com/yjmyzz/archive/2009/11/25/1610253.html

原创粉丝点击