gpt4 book ai didi

c# - .Net 4.5 杀死了我的 TPL,现在呢?

转载 作者:可可西里 更新时间:2023-11-01 03:01:20 24 4
gpt4 key购买 nike

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

知道是什么原因造成的吗?否则,我可以做些什么来尝试进一步缩小问题的根本原因或解决问题?

最佳答案

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

tl;dr 要解决此问题,请强制 CompleteSynchronously 始终返回 false(注意非 lector)。


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

发布这个问题后,我立即点击了 related question , 并结束于 "Application Compatibility in the .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.

Impact: The resulting task will not complete if an IAsyncResult implementation does not complete synchronous execution but its CompletedSynchronously property returns True.

具有讽刺意味的是,(或令人气愤的是)CompletedSynchronously 的页面状态:

Notes to Implementers: Most implementers of the IAsyncResult interface will not use this property and should return false.


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 (...).

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. 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. 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.

Specifically, it’s ok if IAsyncResult.CompletedSynchronously returns false. However, if it returns true, the IAsyncResult must have in fact completed synchronously. 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.

The change was made for performance reasons.


回到我的问题代码

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

The problem appears to be that the library you’re using has a very faulty implementation of IAsyncResult; in particular, it’s implementing CompletedSynchronously incorrectly. 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. But CompletedSynchronously can’t just return that same field: CompletedSynchronously needs to return whether the operation completed synchronously, i.e. whether it completed during the call to BeginXx, and it must always return the same value for a given IAsyncResult instance.

Consider the standard pattern for how IAsyncResult.CompletedSynchronously is used. Its purpose is to allow the caller of BeginXx to continue doing the follow-on work, rather than having the callback due the work. 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):

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. As such, CompletedSynchronously must always return the same value for this particular instance. If it doesn’t, erroneous behavior can easily result. For example, their implementation has CompletedSynchronously returning the equivalent of IsCompleted. So imagine the following sequence of events:

  • BeginXx is called and initiates the async operation
  • BeginXx returns to its caller, which checks CompletedSynchronously, which is false, because the operation hasn’t completed yet.
  • 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.
  • 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. Have you tried just always returning false from CompletedSynchronously instead of always returning true?

关于c# - .Net 4.5 杀死了我的 TPL,现在呢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14889514/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com