簡體   English   中英

CancellationTokenRegistration.Dispose in Async Task

[英]CancellationTokenRegistration.Dispose in Async Task

我正在創建一個SocketExtender類,它將為Socket類提供async/await Task擴展方法。 在我的擴展方法中,我添加的是通過CancellationToken參數取消Socket操作的功能,例如ConnectAsyncReceiveAsyncSendAsync

做一些關於最佳方法的閱讀,我決定將調用包裝在TaskCompletionSource<T>並傳回一個Task來進行操作。

例如,我想將BeginReceiveEndReceive 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM