6

Motivation

C# 5.0 async/await constructs are awesome, unfortunately yet Microsoft only shown a release candidate of both .NET 4.5 and VS 2012, and it will take some time until these technologies will get widely adopted in our projects.

In Stephen Toub's Asynchronous methods, C# iterators, and Tasks I've found a replacement that can be nicely used in .NET 4.0. There are also a dozen of other implementations that make it possible using the approach even in .NET 2.0 though they seem little outdated and less feature-rich.

Example

So now my .NET 4.0 code looks like (the commented sections show how it is done in .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));
    }
}

Looks little ugly though it does the job

The question

When using CancellationTokenSource in .NET 4.5 there is a constructor that takes timespan as a timeout parameter, so that resulting CancellationTokenSource cancels within specified period of time.
.Net 4.0 is not able to timeout, so what is the correct way of doing that in .Net 4.0?

4

3 回答 3

5

FWIW,您可以在 4.0 项目中使用 async/await,只需使用async 目标包。非常适合我!

于 2012-07-02T20:50:08.393 回答
4

这真的与异步/等待有关吗?看起来你只需要一种方法来取消令牌,独立于异步/等待,对吧?在这种情况下,您是否可以简单地创建一个在超时后调用 Cancel 的 Timer?

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 回答