以文本方式查看主题

-  计算机科学论坛  (http://bbs.xml.org.cn/index.asp)
--  『 Dot NET,C#,ASP,VB 』  (http://bbs.xml.org.cn/list.asp?boardid=43)
----  解决线程访问控件时产生的 CrossThread 方法  (http://bbs.xml.org.cn/dispbbs.asp?boardid=43&rootid=&id=122133)


--  作者:卷积内核
--  发布时间:10/20/2011 9:07:00 AM

--  解决线程访问控件时产生的 CrossThread 方法

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;
}


--  作者:卷积内核
--  发布时间:10/20/2011 9:08:00 AM

--  
how to access a control from another thread which didn't create this control.

I faced this issue more than 1 time, I decided to collect info about it and made some changes on the code to simplify this problem to you cause it's really annoying and confusing to work with threading stuff. Here is a small code solves this problem FOREVER and in ANY case.

Error:

Cross thread operation not valid: Control "XXXXXXXXXX" accessed from a thread other than the thread it was created.

Solution:

1- Create your thread:


Private Strt As System.Threading.Thread   


2- Start your thread wherever you want:


  Strt = New System.Threading.Thread(AddressOf MyThread1)
  Strt.Start()


3- Add the thread sub (MyThread1) and put whatever you want and remember that the lines which access a control from this thread will be separated to into another sub (the Delegate sub)


  Sub MyThread1
       ' Working code
       ' Working code
       ' Working code
       ' Working code
       ' Working code
       ' Working code

       AccessControl()

End Sub


From the previous code you will notice 2 things:
1st: AccessControl the sub which will be delegated.
2nd: ' Working code - which doesn't need a delegate to get it work. In other mean, it doesn't show up the error message you receive.

4- and finally, add the delegated sub:


Private Sub AccessControl()
        If Me.InvokeRequired Then
            Me.Invoke(New MethodInvoker(AddressOf AccessControl))
        Else
    ' Code wasn't working in the threading sub
    ' Code wasn't working in the threading sub
    ' Code wasn't working in the threading sub
    ' Code wasn't working in the threading sub
    ' Code wasn't working in the threading sub
            Button2.Visible = True
            Button3.Visible = True
            Opacity = 1
            ShowInTaskbar = True
        End If
    End Sub


From the previous code you will notice that all the codes which wasn't working in the threading sub will be added after "Else" line.
examples for some codes which needs to be delegated:


--  作者:卷积内核
--  发布时间:10/20/2011 9:10:00 AM

--  
This error usually shows when Threading operation or Timers are used. UI Controls are naturally belongs to one thread. So if you have another thread to use then use the following simple steps to avoid Cross-Threading

1- Create your thread:
private Thread _thread = null;
private delegate void CrossThreading();

2- Start your thread wherever you want:
_thread = new Thread(MyThread1);
_thread.Start();


3- Add the thread sub (MyThread1) and put whatever you want and remember that the lines which access a control from this thread will be separated to into another sub (the Delegate sub)

private void MyThread1()
{
    MyController w = new MyController();
    this.MyProperty = w.GetResponse(_oLex.LexName);
            
    PoppingUp();
}


From the previous code you will notice 2 things:
1st: PoppingUp() the sub which will be delegated.
2nd: ' Working code - which doesn't need a delegate to get it work. In other mean, it doesn't show up the error message you receive.

4- and finally, add the delegated sub:

private void PoppingUp()
{
           if (this.InvokeRequired)
           {
                   this.Invoke(new CrossThreading(PoppingUp));
// Call back on UI thread.            }
           else
           {
               // WORKING CODE
               // back to UI thread so change UI controls here

               lblMyLabelControl.Text = this.MyProperty;
           }
}

Now from the previous code you will notice that all the codes which wasn't working in the threading sub will be added after "Else" line.
examples for some codes which needs to be delegated:
ddlLocation.Items.Add(...)



W 3 C h i n a ( since 2003 ) 旗 下 站 点
苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
54.688ms