上次在调试串口的时候,遇到了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);
                MessageBox.Show("返回超时");
 

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

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

上次在调试串口的时候,遇到了Invoke卡死的bug,第一次接触到了BeginInvoke,于是就找了找这两者的区别。Invoke and BeginInvoke这篇文章介绍了windows程序消息机制,有兴趣可以看一下。总结下来就是这么回事:使用BeginInvoke方法封送一个委托方法,类似于使用PostMessage进行通信,这是一个异步方法。也就是该方法封送完毕后马上返回,不会等待...
invoke和begininvoke 区别 一直对invoke和begininvoke的使用和概念比较混乱,这两天看了些资料,对这两个的用法和原理有了些新的认识和理解。 首先说下,invoke和begininvoke的使用有两种情况: 1. control中的invoke、begininvoke。 2. delegrate中的invoke、begininvoke。 这两...
在WPF多线程编程中,经常要在工作线程中更新界面显示,Invoke和BeginInvoke即是为了解决此类问题。 在WPF多线程编程模型中,通过Dispatcher调度程序,来管理UI工作项队列,并拥有应用程序主线程,在大多数WPF应用程序中,只存在一个用户界面线程和一个调度器。 在非UI线程更新UI,通过调用Invoke和BeginInvoke来实现,那么两者有什么区别呢? Invoke是同步更新,会阻塞所在工作者线程,BeginInvoke是异步更新,不会阻塞当前线程。 Invoke调用后
在主线程中开了一个子线程,如果要在子线程中修改主线程某个控件,会触发异常:“线程间操作无效: 从不是创建控件“button1”的线程访问它。”。 1.正确的写法是需要使用InvokeInvoke方法需要创建一个委托。如下所示,我要修改一个Button控件的文字: Thread testThread1=new Thread(new ThreadStart(process1));//主函数中创建一个子线程 testThread1.IsBackground = true; testThread1.Start()
C#中的Invoke和BeginInvoke都是用来在UI线程上执行委托的方法。 Invoke方法会阻塞当前线程,直到委托执行完成。而BeginInvoke方法则是异步执行委托,不会阻塞当前线程。 使用Invoke方法时,如果当前线程是UI线程,则委托会直接在UI线程上执行。如果当前线程不是UI线程,则Invoke方法会将委托加入UI线程的消息队列中,等待UI线程空闲时执行。 使用BeginInvoke方法时,委托会被异步执行,不会阻塞当前线程。当委托执行完成后,会通过回调函数通知调用线程。 总之,Invoke和BeginInvoke都是用来在UI线程上执行委托的方法,只是在执行方式和阻塞方式上有所不同。 ### 回答2: 很抱歉,但是您没有给出关于问题的上下文或内容,因此无法提供有针对性的回答。请提供更多信息或问题的详细描述,以便我可以为您提供更好的帮助。感谢您的理解与配合! ### 回答3: 很抱歉,您的问题不够具体明确,无法给出有意义的回答。如果您可以提供更多的信息或明确您的问题,我们将很乐意为您提供帮助和回答。谢谢! C# 异常:已引发: "线程间操作无效: 从不是创建控件“textBox1”的线程访问它。" (System.InvalidOperationException) there for u: C#根据字符串获取成员变量的值 如果class A 中有两个函数,两个函数中均有high变量(或常量,即同名称),又如何取得特定函数中的high变量值? CubeMX 5.5 修改HAL库库函数版本 NBplusnm: 没,最后我就使用最新的版本来配置 CubeMX 5.5 修改HAL库库函数版本 小菠萝_YAN: 你好,请问你解决这个问题了吗