Ben*_*jol 17 c# task-parallel-library .net-4.5
图表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);
}
Run Code Online (Sandbox Code Playgroud)
安装.Net 4.5(不指向VS,也不重新编译)会阻止这种工作.永远不会调用回调.
任何想法可能导致这种情况,否则,我可以做些什么来尝试进一步缩小问题的根本原因或解决问题?
Ben*_*jol 14
重新编辑:我和Stephen Toub交换了几封电子邮件.下面我尝试将我原来的答案和他的答案合并成一个连贯的整体.
tl; dr来解决这个问题,强迫CompleteSynchronously总是返回false(告诫非lector).
在发布此问题后,我立即点击了相关问题,最后选择了".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不正确.这是他们的实施:Run Code Online (Sandbox Code Playgroud)public bool CompletedSynchronously { get { return _isCompleted; } } public bool IsCompleted { get { return _isCompleted; } }它们的
_isCompleted字段指示异步操作是否已完成,这很好,并且可以从中返回IsCompleted,因为该属性用于指示操作是否已完成.但是CompletedSynchronously不能只返回相同的字段:CompletedSynchronously需要返回操作是否同步完成,即它是否在调用期间完成BeginXx,并且它必须始终为给定IAsyncResult实例返回相同的值.考虑如何
IAsyncResult.CompletedSynchronously使用的标准模式 .其目的是允许调用者BeginXx继续进行后续工作,而不是由于工作而进行回调.这对于避免堆栈潜水尤为重要(想象一下所有实际同步完成的一系列异步操作:如果回调处理了所有工作,那么每个回调都会启动下一个操作,其回调将启动下一个操作,但是因为它们是同步完成的,它们的回调也会作为BeginXx方法的一部分同步调用,因此每次调用都会在堆栈上越来越深,直到它可能溢出):Run Code Online (Sandbox Code Playgroud)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?
| 归档时间: |
|
| 查看次数: |
554 次 |
| 最近记录: |