.Net 4.5杀了我的TPL,现在怎么办?

时间:2013-02-15 06:40:49

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

图1:将异步(非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,也不重新编译)会停止此工作。永远不会调用回调。

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

1 个答案:

答案 0 :(得分:14)

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

tl; dr 要解决此问题,请强制CompleteSynchronously始终返回false(警告非执行者)。


.Net 4.5的MSDN文档&#39;打破&#39;变化

在发布此问题后,我立即点击了related question,最后到了"Application Compatibility in the .NET Framework 4.5",这就是关于FromAsync

  

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

     

影响:如果IAsyncResult实施未完成同步执行,则生成的任务将无法完成   CompletedSynchronously属性返回True。

讽刺的是,(或令人愤怒的),CompletedSynchronously州的页面:

  

对实施者的说明IAsyncResult界面的大多数实施者都不会使用此属性,而应返回 false


<2> 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返回   trueIAsyncResult尚未完成,您有一个需要的错误   要修复,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返回true,你掩盖了这个问题,但是   你可能会引起另一个:如果调用者(在你的情况下,FromAsync)   认为操作已经完成,它将继续   立即调用EndXx方法,该方法将阻塞直到异步   操作已经完成,所以你已经完成了异步操作   进入同步的。你有没有尝试过总是返回false   从CompletedSynchronously而不是总是返回true?