C#跨线程访问

点击“测试”,创建一个线程,从0循环到10000给文本框赋值,代码如下:

private void btn_Test_Click(object sender, EventArgs e)
        {
            //创建一个线程去执行这个方法:创建的线程默认是前台线程
            Thread thread = new Thread(new ThreadStart(Test));
            //Start方法标记这个线程就绪了,可以随时被执行,具体什么时候执行这个线程,由CPU决定
            //将线程设置为后台线程
            thread.IsBackground = true;
            thread.Start();
        }

        private void Test()
        {
            for (int i = 0; i < 10000; i++)
            {
                this.textBox1.Text = i.ToString();
            }
        }

运行结果:

产生错误的原因:textBox1是由主线程创建的,thread线程是另外创建的一个线程,在.NET上执行的是托管代码,C#强制要求这些代码必须是线程安全的,即不允许跨线程访问Windows窗体的控件。

解决方案:

1、在窗体的加载事件中,将C#内置控件(Control)类的CheckForIllegalCrossThreadCalls属性设置为false,屏蔽掉C#编译器对跨线程调用的检查。

private void Form1_Load(object sender, EventArgs e)
 {
        //取消跨线程的访问
        Control.CheckForIllegalCrossThreadCalls = false;
 }

使用上述的方法虽然可以保证程序正常运行并实现应用的功能,但是在实际的软件开发中,做如此设置是不安全的(不符合.NET的安全规范),在产品软件的开发中,此类情况是不允许的。如果要在遵守.NET安全标准的前提下,实现从一个线程成功地访问另一个线程创建的空间,要使用C#的方法回调机制。

2、使用回调函数

回调实现的一般过程:

C#的方法回调机制,也是建立在委托基础上的,下面给出它的典型实现过程。

(1)、定义、声明回调。

1 //定义回调
2 private delegate void DoSomeCallBack(Type para);
3 //声明回调
4 DoSomeCallBack doSomaCallBack;

可以看出,这里定义声明的“回调”(doSomaCallBack)其实就是一个委托。

(2)、初始化回调方法。

doSomeCallBack=new DoSomeCallBack(DoSomeMethod);

所谓“初始化回调方法”实际上就是实例化刚刚定义了的委托,这里作为参数的DoSomeMethod称为“回调方法”,它封装了对另一个线程中目标对象(窗体控件或其他类)的操作代码。

(3)、触发对象动作

Opt  obj.Invoke(doSomeCallBack,arg);

其中Opt obj为目标操作对象,在此假设它是某控件,故调用其Invoke方法。Invoke方法签名为:

object  Control.Invoke(Delegate  method,params  object[] args);

它的第一个参数为委托类型,可见“触发对象动作”的本质,就是把委托doSomeCallBack作为参数传递给控件的Invoke方法,这与委托的使用方式是一模一样的。

最终作用于对象Opt obj的代码是置于回调方法体DoSomeMethod()中的,如下所示:

private void DoSomeMethod(type para)

{

//方法体

Opt obj.someMethod(para);

}

如果不用回调,而是直接在程序中使用“Opt obj.someMethod(para);”,则当对象Opt obj不在本线程(跨线程访问)时就会发生上面所示的错误。

从以上回调实现的一般过程可知:C#的回调机制,实质上是委托的一种应用。在C#网络编程中,回调的应用是非常普遍的,有了方法回调,就可以在.NET上写出线程安全的代码了。

使用方法回调,实现给文本框赋值:

namespace MultiThreadDemo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        //定义回调
        private delegate void setTextValueCallBack(int value);
        //声明回调
        private setTextValueCallBack setCallBack;

        private void btn_Test_Click(object sender, EventArgs e)
        {
            //实例化回调
            setCallBack = new setTextValueCallBack(SetValue);
            //创建一个线程去执行这个方法:创建的线程默认是前台线程
            Thread thread = new Thread(new ThreadStart(Test));
            //Start方法标记这个线程就绪了,可以随时被执行,具体什么时候执行这个线程,由CPU决定
            //将线程设置为后台线程
            thread.IsBackground = true;
            thread.Start();
        }

        private void Test()
        {
            for (int i = 0; i < 10000; i++)
            {
                //使用回调
                textBox1.Invoke(setCallBack, i);
            }
        }

        /// <summary>
        /// 定义回调使用的方法
        /// </summary>
        /// <param name="value"></param>
        private void SetValue(int value)
        {
            this.textBox1.Text = value.ToString();
        }
    }
}

 

赞(2)
未经允许不得转载:TaKaSa » C#跨线程访问

相关推荐

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址