[英]CancellationTokenRegistration.Dispose in Async Task
我正在創建一個SocketExtender
類,它將為Socket
類提供async/await Task
擴展方法。 在我的擴展方法中,我添加的是通過CancellationToken
參數取消Socket
操作的功能,例如ConnectAsync
, ReceiveAsync
或SendAsync
。
做一些關於最佳方法的閱讀,我決定將調用包裝在TaskCompletionSource<T>
並傳回一個Task
來進行操作。
例如,我想將BeginReceive
和EndReceive
APM方法包裝到以下基於Task
的方法中:
public static Task<int> ReceiveAsync(this Socket socket, byte[] buffer, int offset, int count, SocketFlags flags, CancellationToken token)
{
var tcs = new TaskCompletionSource<int>();
socket.BeginReceive(buffer, offset, count, flags, result =>
{
try
{
var bytesReceived = socket.EndReceive(result);
tcs.SetResult(bytesReceived);
}
catch(Exception ex)
{
if(token.IsCancellationRequested)
tcs.SetCanceled(); // <- Yes, this is a typo in the .NET framework! :)
else
tcs.SetException(ex);
}
});
return tcs.Task;
}
注意 :上述方法實際上沒有實現取消! 是的,它會處理它,但它不會導致Socket
實際取消操作。 所以我需要CancellationToken
來通過token.Register()
觸發Socket.Close()
調用。 沒什么大不了的,但問題是如何正確處理返回的CancellationTokenRegistration
對象?
這是我的第一個想法:
public static Task<int> ReceiveAsync(this Socket socket, byte[] buffer, int offset, int count, SocketFlags flags, CancellationToken token)
{
var tcs = new TaskCompletionSource<int>();
using(token.Register(socket.Close)) // <- Here we are ensuring disposal of the CTR
{
socket.BeginReceive(buffer, offset, count, flags, result =>
{
try
{
var bytesReceived = socket.EndReceive(result);
tcs.SetResult(bytesReceived);
}
catch(Exception ex)
{
if(token.IsCancellationRequested)
tcs.SetCanceled(); // <- Yes, this is a typo in the .NET framework! :)
else
tcs.SetException(ex);
}
});
}
return tcs.Task;
}
但是 ,我的問題是,在異步調用完成之前,是否會通過using
處理CancellationTokenRegistration
? 我的第一直覺想說是 ,因為socket.BeginReceive
調用不會阻塞並立即返回,導致using
語句在方法返回tcs.Task
。
但話說回來,也許C#編譯器'理解'我在做什么,並會看到在子范圍內執行一個匿名方法,並且會發生一些黑魔法讓它按照我想要的方式工作。
我甚至需要關心在這里處理CancellationTokenRegistration
嗎? 我應該保留對它的引用並在finally
塊中調用Dispose()
嗎? 或者這會像我希望的那樣工作嗎?
tcs.SetCanceled(); // < - 是的,這是.NET框架中的拼寫錯誤! :)
實際上,美國版的英語版本很奇怪。 英國(和其他)Englishes將使用Cancelled
,但美國英語使用Canceled
。 請注意, Cancellation
始終在每個英語版本中使用兩個l
。
所以我需要CancellationToken通過token.Register()觸發Socket.Close()調用
我不同意。 取消的慣例是僅取消該操作 。 因此,如果你有一個取消令牌的Send
操作,我會驚訝地發現發送取消的語義也會使任何Receive
操作Receive
並進一步使整個套接字無法使用。
在異步調用完成之前,是否會通過使用處理CancellationTokenRegistration?
是。 這里對編譯器的部分沒有神奇的理解。
我甚至需要關心在這里處理CancellationTokenRegistration嗎? 我應該保留對它的引用並在finally塊中調用Dispose()嗎? 或者這會像我希望的那樣工作嗎?
我建議不要在各個操作調用中使用CancellationToken
。 它在邏輯上是一個對象級取消,因此創建一個單獨的類並將其傳遞給構造函數是一個更合適的設計IMO。 但是,如果你堅持, finally
一塊聽起來像是一個很好的方法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.