C# CancellationTokenRegistration.Dispose在异步任务中
我正在制作一个C# CancellationTokenRegistration.Dispose在异步任务中,c#,asynchronous,task,dispose,cancellation,C#,Asynchronous,Task,Dispose,Cancellation,我正在制作一个SocketXtender类,它将为Socket类提供async/await Task扩展方法。在我的扩展方法中,我添加的是通过CancellationToken参数取消Socket操作的能力,例如ConnectAsync、ReceiveAsync或sendaync 在阅读了一些关于执行此操作的最佳方法的资料后,我决定将调用包装在TaskCompletionSource中,并为操作传回一个Task 例如,我想将BeginReceive和EndReceiveAPM方法包装到以下基于T
SocketXtender
类,它将为Socket
类提供async/await Task
扩展方法。在我的扩展方法中,我添加的是通过CancellationToken
参数取消Socket
操作的能力,例如ConnectAsync
、ReceiveAsync
或sendaync
在阅读了一些关于执行此操作的最佳方法的资料后,我决定将调用包装在TaskCompletionSource
中,并为操作传回一个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;
}
然而,我的问题是在异步调用完成之前,取消令牌注册
会通过使用
进行处理吗?我的第一反应是说yes,因为socket.BeginReceive
调用不会立即阻塞并返回,导致using
语句在方法返回tcs.Task
后进行清理
但话说回来,也许C#编译器“理解”了我在做什么,并且会看到在子作用域中执行了一个匿名方法,并且会发生一些黑魔法,使它以我想要的方式工作
我是否需要在这里处理CancellationTokenRegistration
?我是否应该保留对它的引用,并在finally
块中调用Dispose()
?或者这会像我希望的那样工作吗
tcs.setCancelled();//谢谢你的快速回复!经过进一步研究,它似乎确实不是一个打字错误。啊,英语是一种多么离奇的语言啊!至于
CancellationToken
的用法,我明白你的意思。与仅仅关闭套接字相比,使用它没有什么意义。也许使用Connect
或Accept
是有意义的,但不是Send
或Receive
。真正的问题更多的是关于在这种情况下使用的范围,以及对象是否会被过早地处置,似乎就是这样。再次感谢!一种解决方案是为您返回的任务注册一个已取消的继续。只需在返回语句之前插入以下内容:tcs.Task.ContinueWith(=>socket.Close(),TaskContinuationOptions.OnlyOnCanceled)
@MikeStrobel:您仍然存在取消读取会关闭整个套接字的问题,而不仅仅是取消读取。@stephenleary True,但如果中止任何套接字操作,您可能会得到一个部分读取或写入(因此无效)的数据流,这可能会导致连接无效。
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;
}