Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/278.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# CancellationTokenRegistration.Dispose在异步任务中_C#_Asynchronous_Task_Dispose_Cancellation - Fatal编程技术网

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;
}