图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,也不重新编译)会停止此工作。永远不会调用回调。
任何可能导致此问题的想法,以及我可以做些什么来尝试进一步缩小问题的根本原因或解决问题?
答案 0 :(得分:14)
重新编辑:我已与Stephen Toub交换了几封电子邮件。下面我尝试将我原来的答案和他的答案合并成一个连贯的整体。
tl; dr 要解决此问题,请强制CompleteSynchronously
始终返回false(警告非执行者)。
在发布此问题后,我立即点击了related question,最后到了"Application Compatibility in the .NET Framework 4.5",这就是关于FromAsync
:
更改:
IAsyncResult
实施必须同步完成,其CompletedSynchronously
属性必须返回 true 完成任务。影响:如果
IAsyncResult
实施未完成同步执行,则生成的任务将无法完成CompletedSynchronously
属性返回True。
讽刺的是,(或令人愤怒的),CompletedSynchronously
州的页面:
对实施者的说明:
IAsyncResult
界面的大多数实施者都不会使用此属性,而应返回 false 。
表格 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
尚未完成,您有一个需要的错误 要修复,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?