C# Invoke和BeginInvoke的区别

上次在调试串口的时候,遇到了Invoke卡死的bug,第一次接触到了BeginInvoke,于是就找了找这两者的区别。

Invoke and BeginInvoke这篇文章介绍了windows程序消息机制,有兴趣可以看一下。
总结下来就是这么回事:

使用BeginInvoke方法封送一个委托方法,类似于使用PostMessage进行通信,这是一个异步方法。也就是该方法封送完毕后马上返回,不会等待委托方法的执行结束,调用者线程将不会被阻塞。但是调用者也可以使用EndInvoke方法或者其它类似WaitHandle机制等待异步操作的完成。

但是在内部实现上,Invoke和BeginInvoke都是用了PostMessage方法,从而避免了SendMessage带来的问题。而Invoke方法的同步阻塞是靠WaitHandle机制来完成的。

如果用代码来看的话,下面两种写法的效果是完全相同的。

			this.Invoke(new Action(() =>
            {
                textBox1.AppendText(s);
            }));
            IAsyncResult asyncResult = this.BeginInvoke(new Action(() =>
            {
                textBox1.AppendText(s);
            }));
            while (!asyncResult.AsyncWaitHandle.WaitOne())
            {

            }
            this.EndInvoke(asyncResult);   
			IAsyncResult asyncResult = this.BeginInvoke(new Action(() =>
            {
                textBox1.AppendText(s);
            }));
            while (!asyncResult.IsCompleted)
            {

            }
            this.EndInvoke(asyncResult);   

假如代理通知的事情一直没有完成,使用Invoke就会一直阻塞再这里。

考虑到asyncResult.IsCompleted不具有阻塞性,即cpu会一直执行类似while(true)的语句,程序是真的退不出去。而asyncResult.AsyncWaitHandle.WaitOne()还具有多个重载:

		//
        // 摘要: 
        //     阻止当前线程,直到当前 System.Threading.WaitHandle 收到信号。
        //
        // 返回结果: 
        //     如果当前实例收到信号,则为 true。 如果当前实例永远收不到信号,则 System.Threading.WaitHandle.WaitOne(System.Int32,System.Boolean)
        //     永不返回。
        //
        // 异常: 
        //   System.ObjectDisposedException:
        //     当前实例已被释放。
        //
        //   System.Threading.AbandonedMutexException:
        //     线程退出时未释放互斥体,等待过程已终止。 在 Windows 98 或 Windows Millennium Edition 中不引发此异常。
        //
        //   System.InvalidOperationException:
        //     当前实例是另一个应用程序域中的 System.Threading.WaitHandle 的透明代理。
        public virtual bool WaitOne();
        //
        // 摘要: 
        //     阻止当前线程,直到当前 System.Threading.WaitHandle 收到信号,同时使用 32 位带符号整数指定时间间隔。
        //
        // 参数: 
        //   millisecondsTimeout:
        //     等待的毫秒数,或为 System.Threading.Timeout.Infinite (-1),表示无限期等待。
        //
        // 返回结果: 
        //     如果当前实例收到信号,则为 true;否则为 false。
        //
        // 异常: 
        //   System.ObjectDisposedException:
        //     当前实例已被释放。
        //
        //   System.ArgumentOutOfRangeException:
        //     millisecondsTimeout 是一个非 -1 的负数,而 -1 表示无限期超时。
        //
        //   System.Threading.AbandonedMutexException:
        //     线程退出时未释放互斥体,等待过程已终止。 在 Windows 98 或 Windows Millennium Edition 中不引发此异常。
        //
        //   System.InvalidOperationException:
        //     当前实例是另一个应用程序域中的 System.Threading.WaitHandle 的透明代理。
        public virtual bool WaitOne(int millisecondsTimeout);

参考c#线程之异步委托begininvoke、invoke、AsyncWaitHandle.WaitOne 、异步回调BeginInvoke和EndInvoke方法两篇博客,可以使用WaitOne(int millisecondsTimeout)方法。
如果invoke没有返回,则阻塞500ms后,WaitOne()函数返回false,可以做异常处理。
如果500ms以内返回,正常结束Invoke。

            IAsyncResult asyncResult = this.BeginInvoke(new Action(() =>
            {
                textBox1.AppendText(s);
            }));
            if (asyncResult.AsyncWaitHandle.WaitOne(500))
            {
                this.EndInvoke(asyncResult);
            }
            else
            {
                MessageBox.Show("返回超时");
            }

通过一个委托来进行同步方法的异步调用,也是.net提供的异步调用机制之一。但是Delegate.BeginInvoke方法是从ThreadPool取出一个线程来执行这个方法,以获得异步执行效果的。也就是说,如果采用这种方式提交多个异步委托,那么这些调用的顺序无法得到保证。而且由于是使用线程池里面的线程来完成任务,使用频繁,会对系统的性能造成影响。
Delegate.BeginInvoke也是讲一个委托方法封送到其它线程,从而通过异步机制执行一个方法。调用者线程则可以在完成封送以后去继续它的工作。但是这个方法封送到的最终执行线程是运行库从ThreadPool里面选取的一个线程。
这里需要纠正一个误区,那就是Control类上的异步调用BeginInvoke并没有开辟新的线程完成委托任务,而是让界面控件的所属线程完成委托任务的。看来异步操作就是开辟新线程的说法不一定准确。

在这里插入图片描述
实际也测试过,在Action中加Thread.Sleep,会让UI线程1s响应一次,因为这里的BeginInvoke是Control.Invoke,并不是单独开一个线程去执行的Action。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值