Invoke 和 BeginInvoke 的区别
2011-03-03 11:43
435 查看
讨论环境:C# 、.net、VS2005
.net默认所有的可视窗体在主线程内维护,如果某工作线程(主线程之外)想实现对窗体控件的操作,缺省情况下是不允许直接操作的,而要通过 Invoke 方法将其封送到主线程去完成。在Control 类内提供了 Invoke 和 BeginInvoke 两个方法实现该功能,MSDN 帮助中提到,它们的唯一区别是 BeginInvoke 多了“异步执行”四个字。(两方法的具体帮助请自行查看MSDN,这里不多罗嗦了)。
“异步执行”怎么理解,查了网上的一些解答,通过Reflector查看了两方法的背后源码后,得出如下结论:
[b]Invoke 引起工作线程的阻塞,BeginInvoke 不引起工作线程的阻塞。[/b]
具体解释一下:我们先假设称主线程(即窗体控件的拥有者)为A线程,工作线程为B线程,如果B线程需要操作窗体控件,那么就要使用Invoke(或BeginInvoke),将相应的操作通过代理,封送到主线程A(具体的代码实现,不多罗嗦,假设读者已知)。那么.net背后是怎么实现线程间“任务挪移”这一步操作的呢?通过Reflector查看源码后发现,原来 Invoke 将你交给它的委托封装成了一个标准的Windows消息,加进了主线程的消息队列内。回到Invoke 和 BeginInvoke 的区别上来,如果使用 Invoke,那么 B 线程必须等到A线程响应了传送的消息后 才能得到返回值,而如果使用BeginInvoke,则B线程将消息送到A线程后,马上返回,并不一定等待该消息被A线程响应完成。所以如果A线程处在繁忙状态或休眠状态,使用 Invoke 封送消息就会使得B线程被堵塞,而是用 BeginInvoke 则不然,这就是所谓的“异步执行”了。
空口无凭,让我们来看看Invoke和 BeginInvoke 背后的代码:
好长的一段代码,看着都眼晕。看看刚才提到的第三个参数的名字 synchronous ,从字面意思看‘是否同步’,OK,再看看最后被加粗的几行代码(还是根据字面意思猜):如果不同步,立刻返回;如果同步,而且没完事,就等一等,最后给出返回值。如果我们的假设成立,那么 Invoke 方法的第三个参数为True,就是要同步;BeginInvoke 方法的第三个参数为 False,就是不同步,即异步。Right,到这里就和 MSDN 帮助上介绍 BeginInvoke “异步执行”就对上了。
如果按照数学分析归纳法,到此应该说:由以上分析,得证:
[b]Invoke 引起工作线程的阻塞,BeginInvoke 不引起工作线程的阻塞。[/b]
最后,要补充两点:第一,如果主线程以外的工作线程要操作窗体控件,并非一定要使用Invoke方法,更改下边这个属性也是 OK 的,只是不推荐使用。
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;
第二,既然 [b]BeginInvoke 不引起阻塞,那么是否就说明它比 Invoke 好呢。我在自己的程序里,把所有的 Invoke 均变成 BeginInvoke ,运行一段时间后,结果提示 StackOverFlowException 错误,帮助对这个异常的说明是“挂起的方法调用过多而导致执行堆栈溢出时引发的异常”。看来还是要慎用。[/b]
生活TMD需要激情,做事需冷静,说话需冷静!
遇事记着:办法总比困难多,困难和问题说不定就是机遇和转折!
历史证明:哪个环节没照顾到,哪个环节就会出问题!能自己来,就不要让别人来。
.net默认所有的可视窗体在主线程内维护,如果某工作线程(主线程之外)想实现对窗体控件的操作,缺省情况下是不允许直接操作的,而要通过 Invoke 方法将其封送到主线程去完成。在Control 类内提供了 Invoke 和 BeginInvoke 两个方法实现该功能,MSDN 帮助中提到,它们的唯一区别是 BeginInvoke 多了“异步执行”四个字。(两方法的具体帮助请自行查看MSDN,这里不多罗嗦了)。
“异步执行”怎么理解,查了网上的一些解答,通过Reflector查看了两方法的背后源码后,得出如下结论:
[b]Invoke 引起工作线程的阻塞,BeginInvoke 不引起工作线程的阻塞。[/b]
具体解释一下:我们先假设称主线程(即窗体控件的拥有者)为A线程,工作线程为B线程,如果B线程需要操作窗体控件,那么就要使用Invoke(或BeginInvoke),将相应的操作通过代理,封送到主线程A(具体的代码实现,不多罗嗦,假设读者已知)。那么.net背后是怎么实现线程间“任务挪移”这一步操作的呢?通过Reflector查看源码后发现,原来 Invoke 将你交给它的委托封装成了一个标准的Windows消息,加进了主线程的消息队列内。回到Invoke 和 BeginInvoke 的区别上来,如果使用 Invoke,那么 B 线程必须等到A线程响应了传送的消息后 才能得到返回值,而如果使用BeginInvoke,则B线程将消息送到A线程后,马上返回,并不一定等待该消息被A线程响应完成。所以如果A线程处在繁忙状态或休眠状态,使用 Invoke 封送消息就会使得B线程被堵塞,而是用 BeginInvoke 则不然,这就是所谓的“异步执行”了。
空口无凭,让我们来看看Invoke和 BeginInvoke 背后的代码:
public object [b]Invoke(Delegate method, params object[] args)[/b]
{
using (MultithreadSafeCallScope [b]scope = new MultithreadSafeCallScope())[/b]
{
return this.FindMarshalingControl().MarshaledInvoke(this, method, args, true);
}
}
public IAsyncResult [b]BeginInvoke(Delegate method, params object[] args)[/b]
{
using (MultithreadSafeCallScope [b]scope = new MultithreadSafeCallScope())[/b]
{
return (IAsyncResult) this.FindMarshalingControl().MarshaledInvoke(this, method, args, false);
}
}两方法对应的代码基本一样,除了返回类型外,还有一处细微的差别 MarshaledInvoke 方法的第三个参数:Invoke 为 true,BeginInvoke 为 false。这个参数表示什么意思呢?把MarshaledInvoke 背后的代码拉出来看看:
private object [b]MarshaledInvoke(Control caller, Delegate method, object[] args, bool synchronous)[/b]
{
int [b]num;[/b]
if (!this.IsHandleCreated)
{
throw new InvalidOperationException(SR.GetString("ErrorNoMarshalingThread"));
}
if (((ActiveXImpl) this.Properties.GetObject(PropActiveXImpl)) != null)
{
IntSecurity.UnmanagedCode.Demand();
}
bool [b]flag = false;[/b]
if ((SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(this, this.Handle), out num) == SafeNativeMethods.GetCurrentThreadId()) && synchronous)
{
flag = true;
}
ExecutionContext [b]executionContext = null;[/b]
if (!flag)
{
executionContext = ExecutionContext.Capture();
}
ThreadMethodEntry [b]entry = new ThreadMethodEntry(caller, method, args, synchronous, executionContext);[/b]
lock (this)
{
if (this.threadCallbackList == null)
{
this.threadCallbackList = new Queue();
}
}
lock (this.threadCallbackList)
{
if (threadCallbackMessage == 0)
{
threadCallbackMessage = SafeNativeMethods.RegisterWindowMessage(Application.WindowMessagesVersion + "_ThreadCallbackMessage");
}
this.threadCallbackList.Enqueue(entry);
}
if (flag)
{
this.InvokeMarshaledCallbacks();
}
else
{
UnsafeNativeMethods.PostMessage(new HandleRef(this, this.Handle), threadCallbackMessage, IntPtr.Zero, IntPtr.Zero);
}
[b][b]if (!synchronous)[/b][/b]
[b][b] {[/b][/b]
[b][b] return entry;[/b][/b]
[b][b] }[/b][/b]
[b][b] if (!entry.IsCompleted)[/b][/b]
[b][b] {[/b][/b]
[b][b] this.WaitForWaitHandle(entry.AsyncWaitHandle);[/b][/b]
[b][b] }[/b][/b]if (entry.exception != null)
{
throw entry.exception;
}
return entry.retVal;}
好长的一段代码,看着都眼晕。看看刚才提到的第三个参数的名字 synchronous ,从字面意思看‘是否同步’,OK,再看看最后被加粗的几行代码(还是根据字面意思猜):如果不同步,立刻返回;如果同步,而且没完事,就等一等,最后给出返回值。如果我们的假设成立,那么 Invoke 方法的第三个参数为True,就是要同步;BeginInvoke 方法的第三个参数为 False,就是不同步,即异步。Right,到这里就和 MSDN 帮助上介绍 BeginInvoke “异步执行”就对上了。
如果按照数学分析归纳法,到此应该说:由以上分析,得证:
[b]Invoke 引起工作线程的阻塞,BeginInvoke 不引起工作线程的阻塞。[/b]
最后,要补充两点:第一,如果主线程以外的工作线程要操作窗体控件,并非一定要使用Invoke方法,更改下边这个属性也是 OK 的,只是不推荐使用。
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;
第二,既然 [b]BeginInvoke 不引起阻塞,那么是否就说明它比 Invoke 好呢。我在自己的程序里,把所有的 Invoke 均变成 BeginInvoke ,运行一段时间后,结果提示 StackOverFlowException 错误,帮助对这个异常的说明是“挂起的方法调用过多而导致执行堆栈溢出时引发的异常”。看来还是要慎用。[/b]
生活TMD需要激情,做事需冷静,说话需冷静!
遇事记着:办法总比困难多,困难和问题说不定就是机遇和转折!
历史证明:哪个环节没照顾到,哪个环节就会出问题!能自己来,就不要让别人来。
相关文章推荐
- BeginInvoke、ThreadPool、Task三类异步方法的区别和速度比较
- this.Invoke和this.BeginInvoke的区别
- c# Invoke和BeginInvoke 区别
- 引用 c# Invoke和BeginInvoke 区别
- c# Invoke和BeginInvoke 区别
- C#中Invoke 和 BeginInvoke 的区别
- c# Invoke和BeginInvoke 区别
- C# delegate方法invoke和beginInvoke的区别
- C#中invoke和begininvoke 区别
- c# Invoke和BeginInvoke 区别
- Invoke 和 BeginInvoke 的区别
- Invoke 和 BeginInvoke 的区别
- c#多线程(UI线程,控件显示更新) Invoke和BeginInvoke 区别
- c# Invoke和BeginInvoke 区别
- c# Invoke和BeginInvoke 区别
- c# Invoke和BeginInvoke 区别
- 【分析】浅谈C#中Control的Invoke与BeginInvoke在主副线程中的执行顺序和区别(SamWang)
- .c# Invoke和BeginInvoke 区别
- c# Invoke和BeginInvoke 区别
- c#多线程(UI线程,控件显示更新) Invoke和BeginInvoke 区别