博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Control.Invoke和Control.BeginInvoke简述
阅读量:4942 次
发布时间:2019-06-11

本文共 2749 字,大约阅读时间需要 9 分钟。

出自:

1.背景介绍

        在Windows Form中使用多线程时,除了创建控件的线程以外(就是主线程),绝对不要在任何其他线程里面调用控件的成员(只有极个别情况例外),也就是说控件属于创建它的线程,不能从其他线程里面访问。这一条适用于所有从System.Windows.Forms.Control派生的控件(因此可以说是几乎所有控件),包括Form控件本身也是。举一反三,我们很容易得出这样的结论,控件的子控件必须由创建控件的线程来创建,比如一个表单上的按钮,比如由创建表单的线程来创建,因此,一个窗口中的所有控件实际上都活在同一个线程之中。在实际编程时,大多数的软件的做法都是让同一线程负责全部的控件,这就是我们所说的UI线程(就是主线程)。

      由于上述限制,我们可能会感到很不方便,的确,当我们利用一个新创建的线程来执行某些花时间的运算时,怎样知道运算进度如何并通过UI反映给用户呢?解决方法很多!比如熟悉多线程编程的用户很快会想到,我们采用一些低级的同步方法,工作者线程把状态保存到一个同步对象中,让UI线程轮询(Polling)该对象并反馈给用户就可以了。不过,这还是挺麻烦的,实际上不用这样做,Control类(及其派生类)对象有一个Invoke方法很特别,这是少数几个不受线程限制的成员之一。我们前面说到,绝对不要在任何其他线程里面调用非本线程创建的控件的成员时,也说了“只有极个别情况例外”,这个Invoke方法就是极个别情况之一----Invoke方法可以从任何线程里面调用。

2.Invoke,BeginInvoke介绍

      Invoke方法的参数很简单,一个委托,一个参数表(可选),而Invoke方法的主要功能就是帮助你在UI线程(即创建控件的线程)上调用委托所指定的方法。Invoke方法首先检查发出调用的线程(即当前线程)是不是UI线程,如果是,直接执行委托指向的方法,如果不是,它将切换到UI线程,然后执行委托指向的方法。不管当前线程是不是UI线程,Invoke都阻塞直到委托指向的方法执行完毕,然后切换回发出调用的线程(如果需要的话),返回。注意,使用Invoke方法时,UI线程不能处于阻塞状态。以下MSDN里关于Invoke方法的说明:

   “控件上有四种方法可以安全地从任何线程进行调用:Invoke、BeginInvoke、EndInvoke 和CreateGraphics。对于所有其他方法调用,则应使用调用 (invoke) 方法之一封送对控件的线程的调用。
  委托可以是 EventHandler 的实例,在此情况下,发送方参数将包含此控件,而事件参数将包含 EventArgs.Empty。委托还可以是 MethodInvoker 的实例或采用 void 参数列表的其他任何委托。调用 EventHandler 或 MethodInvoker 委托比调用其他类型的委托速度更快。”
     说说BeginInvoke,毫无疑问这是Invoke的异步版本(Invoke是同步完成的),BeginInvoke方法总是使用UI线程。相对Invoke而言,使用BeginInvoke稍稍麻烦一点,但还是那句话,异步比同步效果好,尽管复杂些。比如同步方法可能出现这样一种死锁情况:工作者线程通过Invoke同步调用UI线程里的方法时会阻塞,而万一UI线程正在等待工作者线程做某件事时怎么办?因此,能够使用异步方法时应尽量使用异步方法。
下面是一个具体的例子来说明Invoke和BeginInvoke的区别:

namespace ControlInvoke_BeginInvoke

{
    public partial class Form1 : Form
    {
        private Thread myThread;
        private delegate void DisplayMethod(string content);
        private delegate void BeginInvokeDelegate();
        string strContent = string.Empty;
        public Form1()
        {
            InitializeComponent();
        }

        private void btnStart_Click(object sender, EventArgs e)

        {
            if (myThread != null && myThread.IsAlive)
            {
                this.btnStart.Text = "Stop";
                myThread.Abort();
            }
            else
            {
                this.txtMain.Text = string.Empty;
                string strContent = string.Empty;
                this.btnStart.Text = "Start";
                this.myThread = new Thread(new ThreadStart(GetContent));
                this.myThread.Start();
            }
        }

        private void GetContent()

        {
                this.Invoke(new DisplayMethod(Refresh_DisplayContent), "Hello World");
                MessageBox.Show("BeginInvoke");
        }
        private void Refresh_DisplayContent(string content)
        {
            Thread.Sleep(5000);
            lock (this.strContent)
            {
                this.strContent += content + "/r/n";
            }
            this.txtMain.Text = this.strContent;
            this.txtMain.Refresh();
        }
    }

上面代码执行结果将是画面上先显示"Hello World“,然后再弹出对话框。如果使用this.BeginInvoke

        private void GetContent()
        {
                this.BeginInvoke(new DisplayMethod(Refresh_DisplayContent), "Hello World");
                MessageBox.Show("BeginInvoke");
        }
结果正好相反,由于BeginInvoke为异步操作,即Refresh_DisplayContent方法的执行相对于myThread 线程是异步的。

转载于:https://www.cnblogs.com/xcj26/archive/2011/08/22/2150003.html

你可能感兴趣的文章
remote connect openshift mysql
查看>>
mysql 中if(),left(),right(),with rollup的用法
查看>>
date命令
查看>>
Codeforces205E Little Elephant and Furik and RubikLittle Elephant and Furik and Rubik
查看>>
软件测试的面试
查看>>
1221作业
查看>>
ipython介绍及使用
查看>>
android platform下载地址
查看>>
Skip level 1 on 1
查看>>
【转】常见面试之机器学习算法思想简单梳理
查看>>
OC正则表达式的使用
查看>>
MySQL优化(三):优化数据库对象
查看>>
看到的一个很不错的分析LCA和RMQ的文章(转载,先收着)
查看>>
EXCEL公式及宏
查看>>
组合数学—容斥原理与鸽巢原理
查看>>
中国象棋棋子及棋盘的绘制
查看>>
socketserver剖析.html
查看>>
分享两个网址,一个是使用mssql自带的跟踪工具和分析工具
查看>>
[贪心][高精度][NOIP]国王游戏
查看>>
Java对象创建的过程及对象的内存布局与访问定位
查看>>