6

動機

C#5.0 async / await構造は素晴らしいですが、残念ながらMicrosoftは.NET4.5とVS2012の両方のリリース候補を示しただけであり、これらのテクノロジがプロジェクトで広く採用されるまでにはしばらく時間がかかります。

Stephen Toubの非同期メソッド、C#イテレーター、およびタスクで、 .NET4.0でうまく使用できる代替品を見つけました。他にも、.NET 2.0でもこのアプローチを使用できるようにする実装が多数ありますが、それらは少し古く、機能が豊富ではないように見えます。

これで、私の.NET 4.0コードは次のようになります(コメント付きのセクションは、.NET 4.5でどのように行われるかを示しています)。

//private async Task ProcessMessageAsync()
private IEnumerable<Task> ProcessMessageAsync()
{
    //var udpReceiveResult = await udpClient.ReceiveAsync();

    var task = Task<UdpAsyncReceiveResult>
               .Factory
               .FromAsync(udpClient.BeginReceive, udpClient.EndReceive, null);

    yield return task;

    var udpReceiveResult = task.Result;

    //... blah blah blah

    if (message is BootstrapRequest)
    {
        var typedMessage = ((BootstrapRequest)(message));

        // !!! .NET 4.0 has no overload for CancellationTokenSource that 
        // !!! takes timeout parameter :(
        var cts 
          = new CancellationTokenSource(BootstrapResponseTimeout); // Error here

        //... blah blah blah

        // Say(messageIPEndPoint, responseMessage, cts.Token);

        Task.Factory.Iterate(Say(messageIPEndPoint, responseMessage, cts.Token));
    }
}

それは仕事をしますが少し醜いように見えます

質問

.NET 4.5でCancellationTokenSourceを使用する場合、タイムアウトパラメーターとしてtimespanを受け取るコンストラクターがあり、その結果、CancellationTokenSource指定された期間内にキャンセルされます。
.Net 4.0はタイムアウトできないので、.Net 4.0でタイムアウトする正しい方法は何ですか?

4

3 に答える 3

5

FWIW、4.0プロジェクトでasync / awaitを使用できますが、asyncターゲティングパックを使用するだけです。私にとって素晴らしい作品です!

于 2012-07-02T20:50:08.393 に答える
4

これは本当にasync/awaitと関係がありますか?async / awaitとは関係なく、トークンをキャンセルする方法が必要なようですよね?その場合、タイムアウト後にキャンセルを呼び出すタイマーを簡単に作成できますか?

new Timer(state => cts.Cancel(), null, BootstrapResponseTimeout, Timeout.Infinite);

編集

上記の私の最初の応答は基本的な考え方ですが、より堅牢な解決策は、Is CancellationTokenSource.CancelAfter()リークですか?にあります。(実際には、探しているコンストラクターの.Net 4.5実装)。そのコードに基づいてタイムアウトトークンを作成するために使用できる関数は次のとおりです。

public static CancellationTokenSource CreateTimeoutToken(int dueTime) {
    if (dueTime < -1) {
        throw new ArgumentOutOfRangeException("dueTime");
    }
    var source = new CancellationTokenSource();
    var timer = new Timer(self => {
        ((Timer)self).Dispose();
        try {
            source.Cancel();
        } catch (ObjectDisposedException) {}
    });
    timer.Change(dueTime, -1);
    return source;
}
于 2012-07-02T21:05:08.867 に答える
0

を引き続き使用できます。これは、上記の受け入れられた回答と非常によく似CancelAfter()た拡張方法です。Microsoft.Bcl.Async

これは、F12を押してCancelAfter():の実装を確認したときの参照ソースコードです。

  /// <summary>Cancels the <see cref="T:System.Threading.CancellationTokenSource" /> after the specified duration.</summary>
  /// <param name="source">The CancellationTokenSource.</param>
  /// <param name="dueTime">The due time in milliseconds for the source to be canceled.</param>
  public static void CancelAfter(this CancellationTokenSource source, int dueTime)
  {
    if (source == null)
      throw new NullReferenceException();
    if (dueTime < -1)
      throw new ArgumentOutOfRangeException("dueTime");
    Contract.EndContractBlock();
    Timer timer = (Timer) null;
    timer = new Timer((TimerCallback) (state =>
    {
      timer.Dispose();
      TimerManager.Remove(timer);
      try
      {
        source.Cancel();
      }
      catch (ObjectDisposedException ex)
      {
      }
    }), (object) null, -1, -1);
    TimerManager.Add(timer);
    timer.Change(dueTime, -1);
  }
于 2017-01-20T05:07:53.677 に答える