繁体   English   中英

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

[英].Net 4.5 killed my TPL, now what?

图表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,也不重新编译)会阻止这种工作。 永远不会调用回调。

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

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

tl; dr来解决这个问题,强制CompleteSynchronously总是返回false( 告诫非lector )。


MSDN文档.Net 4.5'破坏'更改

发布此问题后,我立即点击了相关问题 ,最后选择了“.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返回trueIAsyncResult尚未完成,则您有一个需要修复的错误,并且从FromAsync返回的Task可能无法正确完成。

此更改是出于性能原因。


回到我的问题代码

这是他非常有用的分析,我将其全部包含在内,因为它可能对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为true,因此不执行任何后续工作,因为它假定调用者执行了此操作。
  • 现在没有人跑或者会回调。

简而言之,图书馆有一个错误。 如果您更改了CompletedSynchronously以返回true,则屏蔽了此问题,但您可能会导致另一个问题:如果调用者(在您的情况下, FromAsync )认为操作已经完成,它将立即调用EndXx方法,这将阻止,直到异步操作完成,因此您已将异步操作转换为同步操作。 您是否尝试过始终从CompletedSynchronously返回false而不是始终返回true?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM