代码

概要
Windows Forms 控件通常不是thread-safe(直接或间接继承于System.Windows.Forms.Control),因此.NET Framework为防止multithread下对控件的存取可能导致控件状态的不一致,在调试时,CLR-Debugger会抛出一个InvalidOperationException以‘建议‘程序员程序可能存在的风险。
 
问题的关键在于,动机是什么?和由此而来的编程模型的调整。
1. Example
首先,看一个代码实例。该例要完成的工作是由一个Button的Click触发,启动一个Thread(Manual Thread),该Thread的目的是完成设置TextBox的Text’s Property。
1.1 Unsafe access to control
 
Code 1.1
using System;
using System.Configuration;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.IO;
 
namespace WindowsApplication1 {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }
 
        private void unsafeSetTextButton_Click(object sender, EventArgs e) {
            Thread setTextThread = new Thread(new ThreadStart(doWork));
            setTextThread.Start();
        }
 
        private void doWork() {
            string fileName = ".\\test-src.txt";
            if (!File.Exists(fileName)) {
                MessageBox.Show(string.Format("{0} doesn't exist!", fileName),
                    "FileNoFoundException");
                return;
            }
 
            string text = null;
            using (StreamReader reader = new StreamReader(fileName, Encoding.Default)) {
                text = reader.ReadToEnd();
            }
 
            this.textBox1.Text = text;
        }
    }
}
 
在调试时,CLR-Debugger会在以上代码中粗体处将会弹出如下的对话框:

提示说,当前存取控件的thread非创建控件的thread(Main Thread)。
 
 
1.2 What’s mean?
当然,你也可以忽略InvalidOperationException,在非调试的状态下,该异常并不会被抛出,CLR-Debugger监测对Handle的可能存在的不一致地存取,而期望达到更稳健(robust)的代码,这也就是Cross-thread operation not valid后的真正动机。
 
但是,放在面前的选择有二:第一,在某些情况下,我们并不需要这种善意的‘建议‘,而这种建议将在调试时带来了不必要的麻烦;第二,顺应善意的‘建议‘,这也意味着我们必须调整已往行之有效且得心应手的编程模型(成本之一),而这种调整额外还会带来side-effect,而这种side-effect目前,我并不知道有什么简洁优雅的解决之道予以消除(成本之二)。
 
2. The first choice : CheckForIllegalCrossThreadCalls
忽略Cross-thread InvalidOperationException建议,前提假设是我们不需要类似的建议,同时也不想给自己的调试带来过多的麻烦。
 
关闭CheckForIllegalCrossThreadCalls,这是Control class上的一个static property,默认值为flase,目的在于开关是否对Handle的可能存在的不一致存取的监测;且该项设置是具有Application scope的。
 
如果,只需要在某些Form中消除Cross-thread InvalidOperationException建议,可以在Form的.ctor中,InitializeComponent语句后将CheckForIllegalCrossThreadCalls设置为false 。
 
Code 2. - 1
public Form1() {
    InitializeComponent();
 
    Control.CheckForIllegalCrossThreadCalls = false;
}
 
这种方式虽然可以达到忽略Cross-thread InvalidOperationException建议的目的,但代码不能明晰的表达具有Application scope的语义,下面方式能更好的表达Application scope语义而且便于维护。
 
Code 2. - 2
static void Main() {
    Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault( false );
 
Control.CheckForIllegalCrossThreadCalls = false;
 
    Application.Run( new Form1() );
}
 
 
3. The second choice
接受Cross-thread InvalidOperationException善意的建议,这通常是个明智的选择,即使目前没有简洁优雅的code pattern。
 
Code 3. – 1
using System;
using System.Configuration;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.IO;
 
namespace WindowsApplication1 {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
 
            //Control.CheckForIllegalCrossThreadCalls = false;
        }
 
        private void safeSetTextButton_Click(object sender, EventArgs e) {
            Thread safeSetTextThread = new Thread(new ThreadStart(doWork));
            safeSetTextThread.Start();
        }
 
        private void doWork() {
            string fileName = ".\\test-src.txt";
            if (!File.Exists(fileName)) {
                MessageBox.Show(string.Format("{0} doesn't exist!", fileName),
                    "FileNoFoundException");
                return;
            }
 
            string text = null;
            using (StreamReader reader = new StreamReader(fileName, Encoding.Default)) {
                text = reader.ReadToEnd();
            }
 
            //this.textBox1.Text = text;
            safeSetText(text);
        }
 
        private void safeSetText(string text) {
            if (this.textBox1.InvokeRequired) {
                _SafeSetTextCall call = delegate(string s) {
                    this.textBox1.Text = s;
                };
 
                this.textBox1.Invoke(call, text);
            }
            else
                this.textBox1.Text = text;
        }
 
        private delegate void _SafeSetTextCall(string text);
    }
}
其中主要利用System.ComponentModel.IsynchronizeInvoke的InvokeRequired和Invoke 方法(System.Windows.Forms.Control继承于此),该code pattern对于大多数Windows控件有效 ;这样做的目的是保证由创建控件的Main Thread唯一性地呼叫get_Handle。(注意Code 3. -1 中的粗体 safeSetText方法)
 
但,System.Windows.Forms中ToolStripItem继承链上的控件并不具有后向兼容性,因此以上code pattern对此类控件不适用;可以将以上code pattern改为如下:
        private void safeSetText(string text) {
            if (this.InvokeRequired) {
                _SafeSetTextCall call = delegate(string s) {
                    this.textBox1.Text = s;
                };
 
                this.Invoke(call, text);
            }
            else
                this.textBox1.Text = text;
        }
 
        private delegate void _SafeSetTextCall(string text);
 
因为System.Windows.Form继承System.Windows.Control,可以保证以上代码可以正确编译也能正常按期望工作,这样一来,代码的弹性会好些。
 
国外有兄弟利用Reflection技术将设置单一属性(Property)完全动态化了,代码的弹性因此也更好,但我不鼓励这种做法。理由有二:第一,之所以采用Multithread是因为需要更好的UI反应(interactive)、或者更好的性能、或者两者都要,在这种前提下,Reflection似乎与目标背道而驰;第二,目前这种实现技术所带来的代码弹性的提升非常有限;不过有兴趣的,可以自己验证一下。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/leomaya/archive/2006/12/26/1463695.aspx

转载于:https://www.cnblogs.com/GCHost/archive/2010/02/11/1667545.html

未从创建控件的线程访问解决办法相关推荐

  1. 黄聪:BackGroundWorker解决“线程间操作无效: 从不是创建控件的线程访问它” (C# VS2008)...

    在编程中经常会遇到在一个按钮中执行复杂操作,并将复杂操作最后返回的值加入一个ListView或ComboBox中候选.这个时候程序会卡,当程序员将这些卡代码放进线程(Thread)中后发现当对控件操作 ...

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

    转自原文 线程间操作无效: 从不是创建控件的线程访问它. using System; using System.Collections.Generic; using System.ComponentM ...

  3. 用委托在listbox中异步显示信息,解决线程间操作无效,从不是创建控件的线程访问它...

    //创建一个委托,是为访问listbox控件服务的.public delegate void UpdateTxt(string msg);//定义一个委托变量public UpdateTxt upda ...

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

    经典解决"线程间操作无效: 从不是创建控件的线程访问它" 在编程中经常会遇到在一个按钮中执行复杂操作,并将复杂操作最后返回的值加入一个ListView或ComboBox中候选.这个 ...

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

    private void button1_Click(object sender, EventArgs e){//报错:从不是创建控件的线程访问它Thread t = new Thread(() =& ...

  6. c#报错 :System . Invalid Operation Exception:“线程间操作无效: 从不是创建控件的线程访问它

    一.问题来源 跨线程操作时会报错:System.InvalidOperationException:"线程间操作无效: 从不是创建控件的线程访问它. 二.问题代码 using System; ...

  7. 线程间操作无效:从不是创建控件的线程访问它的三种方法

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

  8. c# 线程间操作无效: 从不是创建控件“”的线程访问它,用托管来解决

    如果代码是在VS2003中的话应该不会抛出这个异常,只有在使用VS2005的时候,如果你从非创建这个控件的线程中访问这个控件或者操作这个控件的话就会抛出这个异常.这是微软为了保证线程安全以及提高代码的 ...

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

    最近在开发项目时用到了操作线程的问题,在新线程中写入了操作控件的代码如: this.btnToolUpload.Enabled = true; 但是在线程执行过程中,当执行到这段代码时提示:" ...

最新文章

  1. 大二上学数据结构和操作系统_毕业后的工作比上学要重要得多。 这是数据。...
  2. 在线作图丨做一张叠加mantel test的相关性热图
  3. Emacs Org-mode学习笔记
  4. PHP变参函数的实现
  5. 【Linux】35. python脚本重命名各子目录下的图片
  6. 2009年网页设计10大趋势
  7. 大班科学计算机的发明应用教案,大班科学教案:机器人探密
  8. Spring boot + maven
  9. 《关于Win10系统下Oculus Senser USB无法识别的问题》
  10. attempt to write a readonly database 错误
  11. 高中python公开课怎么上好_Python公开课 - Requests高级功能
  12. Autodesk Eagle入门之-开启旅程
  13. 《复杂网络理论及应用》
  14. java复制文件的4种方式及拷贝文件到另一个目录下的实例代码
  15. c语言字符串逆序输出reverse,将一个字符串逆序输出
  16. 转载 计算广告 03
  17. Python中的角度转换功能
  18. 博客管理系统测试用例设计——XMind版和网页版
  19. html视频设置自动播放下一个,在html5中,如何使用video标签让两个不同的视频文件按顺序自动播放?...
  20. nfc pm3 模拟加密门禁卡_关于nfc模拟加密门禁卡详细教程(后附软件链接)

热门文章

  1. JS:ES10新特性
  2. myeclipse java maven web 项目结构_MyEclipse + Maven开发springMVC的WEB工程的详细配置过程...
  3. 又要辞职了,又要换工作了
  4. 60后即将退休的人,有多少存款就算富有了?
  5. 一次性存入多少钱就可以有资格跟银行商谈利息了?
  6. 所以進入到二十一世紀之後
  7. 地球绕太阳一圈有多远
  8. 反射真的存在性能问题吗?
  9. 数据结构和算法———P2 算法概述
  10. oracle停止一切进程,oracle中expdp/impdp进程如何停止