为什么发布到WindowsFormsSynchronizationContext时动态调用SendOrPostCallback委托?

时间:2012-12-07 07:29:54

标签: c# .net winforms synchronization synchronizationcontext

我正在编写一个包装器,允许我在任意SynchronizationContext上调用方法,例如允许我将与网络相关的回调(例如Socket.BeginReceive)排队到某个任意调度程序/处理程序,例如作为UI线程,或者我自己为串行执行而设计的实现。 (以避免需要同步数据结构 - 锁等...)。

基本上是这样的:

..
public void BeginInvoke(MethodCall methodCall)
{
    this.synchronizationContext.Post(this.SynchronizationContextCallback, methodCall);
}

private void SynchronizationContextCallback(Object methodCall)
{
    (methodCall as MethodCall).Invoke();
}
..

这一切似乎与WindowsFormsSynchronizationContext一样正常但是当抛出异常时(A System.Reflection.TargetInvocationException)我意识到SendOrPostCallback委托被动态调用(?!)这里是来自System.Windows.Forms.Control.ThreadMethodEntry类的相关代码(Microsoft参考):

private static void InvokeMarshaledCallbackDo(ThreadMethodEntry tme)
{
    // We short-circuit a couple of common cases for speed.
    //
    if (tme.method is EventHandler)
    {
        if (tme.args == null || tme.args.Length < 1)
        {
            ((EventHandler)tme.method)(tme.caller, EventArgs.Empty);
        }
        else if (tme.args.Length < 2)
        {
            ((EventHandler)tme.method)(tme.args[0], EventArgs.Empty);
        }
        else
        {
            ((EventHandler)tme.method)(tme.args[0], (EventArgs)tme.args[1]);
        }
    }
    else if (tme.method is MethodInvoker)
    {
        ((MethodInvoker)tme.method)();
    }
    else if (tme.method is WaitCallback)
    {
        Debug.Assert(tme.args.Length == 1,
                        "Arguments are wrong for WaitCallback");
        ((WaitCallback)tme.method)(tme.args[0]);
    }
    else
    {
        tme.retVal = tme.method.DynamicInvoke(tme.args);
    }
} 

似乎不支持SendOrPostCallback委托,但有趣的是要注意其签名与WaitCallback完全相同!或更一般地,Action<Object>。这让我有疑问,我在这里做错了吗?或者这是设计? (在语言和框架层面,我的意思是..)。显然,动态调用所有调度的方法调用会慢得多,而且调试起来会更困难吗? (我甚至可能找不到这个可用的解决方案?)。我在这里缺少什么?

1 个答案:

答案 0 :(得分:0)

这是不可避免的,它可以封送任何委托类型。唯一的方法是使用DynamicInvoke()。他们确实努力挑选你在Winforms编程中使用的一些常见代理类型,但这绝不是一个详尽的列表而不会失去这样做的好处。担心成本几乎没有意义,这总是涉及线程上下文切换以及响应PostMessage()的Winforms消息循环的延迟。与此相比,DynamicInvoke()的成本是小土豆。

是的,你必须做一些事情以避免被最外层的异常打耳光,TargetInvocationException对任何人都没用。 Winforms Control.Invoke()方法通过仅重新抛出最内部的InnerException来实现。这不是理想的,但比TIE更好。