简体   繁体   English

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

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

Exhibit 1: some code wrapping an Async (not async !) network call into a Task 图表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);
}

Installing .Net 4.5 (not pointing VS at it, nor recompiling) stops this working. 安装.Net 4.5(不指向VS,也不重新编译)会阻止这种工作。 The callback is never invoked. 永远不会调用回调。

Any ideas what could be causing this, and otherwise, what I can do to try and further narrow down the root cause of the problem or work around it? 任何想法可能导致这种情况,否则,我可以做些什么来尝试进一步缩小问题的根本原因或解决问题?

Re-edit : I've exchanged several emails with Stephen Toub . 重新编辑 :我和Stephen Toub交换了几封电子邮件。 Below I attempt to merge my original answer and his answers into a coherent whole. 下面我尝试将我原来的答案和他的答案合并成一个连贯的整体。

tl;dr to work around this, force CompleteSynchronously to always return false ( caveat non-lector ). tl; dr来解决这个问题,强制CompleteSynchronously总是返回false( 告诫非lector )。


MSDN documentation of .Net 4.5 'breaking' changes MSDN文档.Net 4.5'破坏'更改

Immediately after posting this question, I clicked through to a related question , and ended up at "Application Compatibility in the .NET Framework 4.5" , which has this to say about FromAsync : 发布此问题后,我立即点击了相关问题 ,最后选择了“.NET Framework 4.5中的应用程序兼容性” ,其中有关于FromAsync

Change : The IAsyncResult implementation must complete synchronously and its CompletedSynchronously property must return true for the resulting task to complete. 更改IAsyncResult实现必须同步完成,并且其CompletedSynchronously属性必须返回true才能完成生成的任务。

Impact : The resulting task will not complete if an IAsyncResult implementation does not complete synchronous execution but its CompletedSynchronously property returns True. 影响 :如果IAsyncResult实现未完成同步执行但其CompletedSynchronously属性返回True,则生成的任务将无法完成。

Ironically, (or infuriatingly), the page for CompletedSynchronously states: 具有讽刺意味的是,(或令人愤怒的), CompletedSynchronously的页面指出:

Notes to Implementers : Most implementers of the IAsyncResult interface will not use this property and should return false . 对实施者的说明IAsyncResult接口的大多数实现者都不会使用此属性,并且应该返回false


Stephen Toub clarified this with the following: Stephen Toub用以下内容澄清了这一点:

The table at http://msdn.microsoft.com/en-us/library/hh367887%28v=VS.110%29.aspx#core , and specifically the description of the “Change”, is wrong (...). http://msdn.microsoft.com/en-us/library/hh367887%28v=VS.110%29.aspx#core上的表格,特别是“更改”的描述,是错误的(...) 。

There was a change in .NET 4.5 to FromAsync , but it wasn't that all IAsyncResult.CompletedSynchronously implementations must return true: that wouldn't make any sense. .NET 4.5对FromAsync进行了更改,但并非所有IAsyncResult.CompletedSynchronously实现都必须返回true:这没有任何意义。 The change was that FromAsync actually looks at the IAsyncResult's CompletedSynchronously now (it didn't look at it at all in .NET 4), and thus it expects it to be accurate. 变化是FromAsync实际上现在正在查看IAsyncResult's CompletedSynchronously (它在.NET 4中根本没有看到它),因此它期望它是准确的。 As such, if you had a buggy IAsyncResult implementation, FromAsync might still have worked in .NET 4, whereas with .NET 4.5, it's less likely to work with a buggy implementation. 因此,如果你有一个错误的IAsyncResult实现, FromAsync可能仍然在.NET 4中工作,而使用.NET 4.5,它不太可能使用错误的实现。

Specifically, it's ok if IAsyncResult.CompletedSynchronously returns false . 具体来说,如果IAsyncResult.CompletedSynchronously返回false However, if it returns true , the IAsyncResult must have in fact completed synchronously. 但是,如果它返回true ,则IAsyncResult实际上必须同步完成。 If CompletedSynchronously returns true but the IAsyncResult has not completed, you have a bug that needs to be fixed, and it's likely that the Task returned from FromAsync will not complete correctly. 如果CompletedSynchronously返回trueIAsyncResult尚未完成,则您有一个需要修复的错误,并且从FromAsync返回的Task可能无法正确完成。

The change was made for performance reasons. 此更改是出于性能原因。


Returning to my problem code 回到我的问题代码

Here is his very helpful analysis, which I include in full as it will probably be useful for other implementers of IAsyncResult : 这是他非常有用的分析,我将其全部包含在内,因为它可能对IAsyncResult其他实现者有用:

The problem appears to be that the library you're using has a very faulty implementation of IAsyncResult ; 问题似乎是你正在使用的库有一个非常错误的IAsyncResult实现; in particular, it's implementing CompletedSynchronously incorrectly. 特别是,它正在错误地实现CompletedSynchronously Here's their implementation: 这是他们的实施:

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

Their _isCompleted field indicates whether the asynchronous operation has completed, which is fine, and it's fine to return this from IsCompleted , since that property is meant to indicate whether the operation completed or not. 它们的_isCompleted字段指示异步操作是否已完成,这很好,并且可以从IsCompleted返回它,因为该属性用于指示操作是否已完成。 But CompletedSynchronously can't just return that same field: CompletedSynchronously needs to return whether the operation completed synchronously, ie whether it completed during the call to BeginXx , and it must always return the same value for a given IAsyncResult instance. CompletedSynchronously不能只返回相同的字段: CompletedSynchronously需要返回操作是否同步完成,即它是否在调用BeginXx期间完成,并且它必须始终为给定的IAsyncResult实例返回相同的值。

Consider the standard pattern for how IAsyncResult.CompletedSynchronously is used. 考虑如何使用IAsyncResult.CompletedSynchronously的标准模式。 Its purpose is to allow the caller of BeginXx to continue doing the follow-on work, rather than having the callback due the work. 其目的是允许BeginXx的调用者继续执行后续工作,而不是由于工作而进行回调。 This is particularly important for avoiding stack dives (imagine a long sequence of asynchronous operations that all actually completed synchronously: if the callbacks handled all of the work, then each callback would initiate the next operation, whose callback would initiate the next operation, but because they were completing synchronously, their callbacks would also be invoked synchronously as part of the BeginXx methods, so each call would get deeper and deeper on the stack, until it potentially overflowed): 这对于避免堆栈潜水尤为重要(想象一下所有实际同步完成的一系列异步操作:如果回调处理了所有工作,那么每个回调都会启动下一个操作,其回调将启动下一个操作,但是因为它们是同步完成的,它们的回调也将作为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 } 

Note that both the caller and the callback use the same CompletedSynchronously property to determine which of them runs the callback. 请注意,调用者和回调都使用相同的CompletedSynchronously属性来确定哪些运行回调。 As such, CompletedSynchronously must always return the same value for this particular instance. 因此, CompletedSynchronously必须始终为此特定实例返回相同的值。 If it doesn't, erroneous behavior can easily result. 如果没有,则很容易导致错误的行为。 For example, their implementation has CompletedSynchronously returning the equivalent of IsCompleted . 例如,它们的实现具有CompletedSynchronously返回等效的IsCompleted So imagine the following sequence of events: 想象一下以下一系列事件:

  • BeginXx is called and initiates the async operation BeginXx并启动异步操作
  • BeginXx returns to its caller, which checks CompletedSynchronously , which is false, because the operation hasn't completed yet. BeginXx返回其调用者,该调用者检查CompletedSynchronously ,这是错误的,因为操作尚未完成。
  • Now the operation completes and the callback is invoked. 现在操作完成并调用回调。 The callback sees that CompletedSynchronously is true, and so doesn't do any of the subsequent work, because it assumes the caller did it. 回调看到CompletedSynchronously为true,因此不执行任何后续工作,因为它假定调用者执行了此操作。
  • And now no one's run or will run the callback. 现在没有人跑或者会回调。

In short, the library has a bug. 简而言之,图书馆有一个错误。 If you changed CompletedSynchronously to return true, you masked this problem, but you likely caused another: if the caller (in your case, FromAsync ) thinks that the operation has already completed, it'll proceed to immediately call the EndXx method, which will block until the async operation has completed, so you've turned your asynchronous operations into synchronous ones. 如果您更改了CompletedSynchronously以返回true,则屏蔽了此问题,但您可能会导致另一个问题:如果调用者(在您的情况下, FromAsync )认为操作已经完成,它将立即调用EndXx方法,这将阻止,直到异步操作完成,因此您已将异步操作转换为同步操作。 Have you tried just always returning false from CompletedSynchronously instead of always returning true? 您是否尝试过始终从CompletedSynchronously返回false而不是始终返回true?

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

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