【问题标题】:Why is a SendOrPostCallback delegate dynamically invoked when posted to a WindowsFormsSynchronizationContext?为什么发送到 WindowsFormsSynchronizationContext 时会动态调用 SendOrPostCallback 委托?
【发布时间】:2012-12-07 07:29:53
【问题描述】:

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

基本上是这样的:

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

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

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

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&lt;Object&gt;。这让我产生疑问,我在这里做错了吗?还是这是设计使然? (我的意思是在语言和框架级别上......)。显然.. 动态调用所有已调度的方法调用会明显变慢并且更难以调试? (到了我什至可能找不到可用的解决方案的地步?)。我在这里错过了什么?

【问题讨论】:

    标签: c# .net winforms synchronization synchronizationcontext


    【解决方案1】:

    这是不可避免的,它可以编组任何委托类型。唯一的方法是使用 DynamicInvoke()。他们确实努力挑选了一些您将在 Winforms 编程中使用的常见委托类型,但这绝不可能是一个详尽的列表,而不会失去这样做的好处。不必担心成本,这总是涉及线程上下文切换和 Winforms 消息循环响应 PostMessage() 的延迟。与之相比,DynamicInvoke() 的成本是小菜一碟。

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

    【讨论】:

    • 感谢您的回答!虽然我仍然觉得很奇怪 SendOrPostCallback isn't 被认为是“您在 Winforms 编程中使用的常见委托类型”.. 如果可能的解决方案:也许我会添加支持对于 ISynchronizeInvoke? (旧界面?)。无论如何..我会尽力避免那些 TargetInvocationException 错误(这似乎也减少了堆栈跟踪)
    • 刚刚找到解决TargetInvocationException问题的办法!在 VS 选项中:调试 -> 常规启用“当异常跨越 AppDomain 或托管/本机边界时中断”。现在调试器正确地捕获了未处理的异常!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-10-05
    • 2016-09-01
    • 1970-01-01
    • 2011-07-11
    • 2016-04-09
    • 2011-03-01
    • 1970-01-01
    相关资源
    最近更新 更多