【问题标题】:.Net 4.5 killed my TPL, now what?.Net 4.5 杀死了我的 TPL,现在怎么办?
【发布时间】:2013-01-31 02:55:29
【问题描述】:

图表 1:一些代码将 Async(不是 async!)网络调用包装成 Task

public static Task<byte[]> GetAsync(IConnection connection, uint id)
{
    ReadDataJob jobRDO = new ReadDataJob();

    //No overload of FromAsync takes 4 extra parameters, so we have to wrap
    // Begin in a Func so that it looks like it takes no parameters except 
    // callback and state
    Func<AsyncCallback, object, IAsyncResult> wrapped = (callback, state) =>
                jobRDO.Begin(connection, 0, 0, id, callback, state);

    return Task<byte[]>.Factory.FromAsync(wrapped, ar =>
    {
        ErrorCode errorCode;
        UInt32 sError;
        UInt32 attribute;
        byte[] data = new byte[10];
        jobRDO.End(out errorCode, out sError, out attribute, out data);
        if(error != ErrorCode.NO_ERROR)  throw new Exception(error.ToString());
        return data;
    }, jobRDO);
}

安装 .Net 4.5(不将 VS 指向它,也不重新编译)会停止此工作。永远不会调用回调。

任何想法可能导致此问题,否则,我可以做些什么来尝试进一步缩小问题的根本原因或解决它?

【问题讨论】:

  • 找到最后一项,但我不知道如何解释它:FromAsync:"IAsyncResult 实现必须同步完成,其 CompletedSynchronously 属性必须为结果返回 true要完成的任务。”(表中的最后一项)
  • 我怀疑这是一个框架版本问题。您是否使用任何网络调试工具?您应该检查它是否至少在执行请求。我用 .net 4.5 做了很多异步包装,没问题!
  • 哈哈,看看这个,如果你点击我之前评论中的链接,然后点击“CompleteSynchronously”,文字内容为:“IAsyncResult接口的大多数实现者不会使用这个属性并且应该返回 false。”
  • 所以我的包装很幸运,因为它们都返回了 true

标签: c# task-parallel-library .net-4.5


【解决方案1】:

重新编辑:我与Stephen Toub 交换了几封电子邮件。下面我尝试将我的原始答案和他的答案合并为一个连贯的整体。

tl;dr 要解决此问题,请强制 CompleteSynchronously 始终返回 false(caveat non-lector)。


.Net 4.5“重大”更改的 MSDN 文档

在发布这个问题后,我立即点击了related question,并最终到达"Application Compatibility in the .NET Framework 4.5",其中有关于FromAsync的说法:

更改IAsyncResult 实现必须同步完成,其CompletedSynchronously 属性必须返回true 以完成生成的任务。

影响:如果IAsyncResult 实现未完成同步执行但其同步执行,则生成的任务将不会完成 CompletedSynchronously 属性返回 True。

具有讽刺意味的是,(或令人愤怒地)CompletedSynchronously 的页面指出:

实施者注意事项IAsyncResult 接口的大多数实施者不会使用此属性,应返回 false


Stephen Toub 对此进行了澄清:

桌子在 http://msdn.microsoft.com/en-us/library/hh367887%28v=VS.110%29.aspx#core, 特别是“更改”的描述是错误的(...)。

.NET 4.5 更改为 FromAsync,但这还不是全部 IAsyncResult.CompletedSynchronously 实现必须返回 true: 那没有任何意义。变化是 FromAsync 实际上 现在看IAsyncResult’s CompletedSynchronously(看起来不像 在 .NET 4) 中完全没有问题,因此它希望它是准确的。作为 这样,如果你有一个错误的IAsyncResult 实现,FromAsync 可能 仍然在 .NET 4 中工作,而在 .NET 4.5 中,它不太可能 使用有缺陷的实现。

具体来说,如果IAsyncResult.CompletedSynchronously返回就可以了 false。但是,如果它返回true,则IAsyncResult 必须包含在 事实同步完成。如果CompletedSynchronously 返回 true 但是IAsyncResult 还没有完成,你有一个bug 需要 待修复,Task 很可能是从FromAsync 返回的 将无法正确完成。

出于性能原因进行了更改。


回到我的问题代码

这是他非常有用的分析,我将其完整包含在内,因为它可能对IAsyncResult 的其他实现者有用:

问题似乎是你正在使用的库有一个非常 IAsyncResult 的错误实现;特别是,它是 错误地执行CompletedSynchronously。这是他们的 实现:

public bool CompletedSynchronously
{
    get { return _isCompleted; }
}
public bool IsCompleted
{
    get { return _isCompleted; }
}

他们的_isCompleted字段表示是否异步操作 已完成,很好,可以从 IsCompleted,因为该属性旨在指示是否 操作完成与否。但是CompletedSynchronously 不能只是 返回相同的字段:CompletedSynchronously 需要返回是否 操作同步完成,即是否完成 在调用BeginXx 期间,它必须始终返回相同的值 对于给定的IAsyncResult 实例。

考虑如何使用标准模式 使用IAsyncResult.CompletedSynchronously。其目的是允许 BeginXx 的调用者继续做后续工作,而不是 而不是因为工作而回调。这是特别重要的 为了避免堆栈潜水(想象一长串的异步 所有实际同步完成的操作:如果回调 处理所有工作,然后每个回调将启动下一个 操作,其回调将启动下一个操作,但是 因为它们是同步完成的,所以它们的回调也会 作为BeginXx 方法的一部分同步调用,因此每次调用 在堆栈上会越来越深,直到它可能 溢出):

IAsyncResult ar = BeginXx(…, delegate(IAsyncResult iar) =>
{
    if (iar.CompletedSynchronously) return;
    … // do the completion work, like calling EndXx and using its result
}, …);
if (ar.CompletedSynchronously)
{
    … // do the completion work, like calling EndXx and using its result
}

请注意,调用者和回调都使用相同的 CompletedSynchronously 属性来确定它们中的哪一个运行 打回来。因此,CompletedSynchronously 必须始终返回相同的 此特定实例的值。如果没有,错误的行为 很容易产生。例如,他们的实现有 CompletedSynchronously 返回 IsCompleted 的等价物。所以 想象以下事件序列:

  • BeginXx 被调用并启动异步操作
  • BeginXx返回给它的调用者,它检查CompletedSynchronously,这是假的,因为操作没有 尚未完成。
  • 现在操作完成并调用回调。回调看到CompletedSynchronously 是真的,所以不是 做任何后续工作,因为它假定调用者做了它。
  • 现在没有人运行或将运行回调。

简而言之,该库有一个错误。如果你改变了 CompletedSynchronously 返回真,你掩盖了这个问题,但是 你可能引起了另一个:如果调用者(在你的情况下,FromAsync) 认为操作已经完成,它会继续 立即调用EndXx 方法,该方法将阻塞直到异步 操作已经完成,所以你已经开启了异步操作 成同步的。您是否尝试过总是返回 false 来自CompletedSynchronously 而不是总是返回true?

【讨论】:

  • 大家好,这只会影响 IAsyncResult 实现有问题的代码。如果您遇到此问题,请通过 Microsoft dot com 的 netfx45compat 联系我们。我们想了解您的情况。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-13
  • 1970-01-01
相关资源
最近更新 更多