[英].Net 4.5 killed my TPL, now what?
圖表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);
}
安裝.Net 4.5(不指向VS,也不重新編譯)會阻止這種工作。 永遠不會調用回調。
任何想法可能導致這種情況,否則,我可以做些什么來嘗試進一步縮小問題的根本原因或解決問題?
重新編輯 :我和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
尚未完成,則您有一個需要修復的錯誤,並且從FromAsync
返回的Task
可能無法正確完成。此更改是出於性能原因。
這是他非常有用的分析,我將其全部包含在內,因為它可能對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
為true,因此不執行任何后續工作,因為它假定調用者執行了此操作。- 現在沒有人跑或者會回調。
簡而言之,圖書館有一個錯誤。 如果您更改了
CompletedSynchronously
以返回true,則屏蔽了此問題,但您可能會導致另一個問題:如果調用者(在您的情況下,FromAsync
)認為操作已經完成,它將立即調用EndXx
方法,這將阻止,直到異步操作完成,因此您已將異步操作轉換為同步操作。 您是否嘗試過始終從CompletedSynchronously
返回false而不是始終返回true?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.