重新编辑:我与Stephen Toub交换了几封电子邮件。下面我尝试将我的原始答案和他的答案合并为一个连贯的整体。
tl;博士解决这个问题,强制CompleteSynchronously
总是返回 false (警告非演讲者)。
.Net 4.5“重大”更改的 MSDN 文档
发布此问题后,我立即点击了一个相关问题,最后看到了“.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 对 .NET 进行了更改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
字段指示异步操作是否已完成,这很好,并且可以从 中返回 this
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
方法,该方法将阻塞直到异步操作已完成,因此您已将异步操作转换为同步操作。您是否尝试过始终返回 falseCompletedSynchronously
而不是始终返回 true?