简体   繁体   English

在.Net 4.0中使用差的mans async / await构造实现异步超时

[英]Implementing async timeout using poor mans async/await constructs in .Net 4.0

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. C#5.0异步/等待构造非常棒,但遗憾的是,微软只展示了.NET 4.5和VS 2012的候选版本,这些技术将在我们的项目中得到广泛采用需要一些时间。

In Stephen Toub's Asynchronous methods, C# iterators, and Tasks I've found a replacement that can be nicely used in .NET 4.0. 在Stephen Toub的异步方法,C#迭代器和任务中,我找到了一个可以在.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. 还有十几个其他实现甚至可以在.NET 2.0中使用该方法,尽管它们看起来很少过时且功能较少。

Example

So now my .NET 4.0 code looks like (the commented sections show how it is done in .NET 4.5): 所以现在我的.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));
    }
}

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.5中使用CancellationTokenSource时 ,有一个构造函数将时间跨度作为超时参数,以便在指定的时间段内取消生成的CancellationTokenSource
.Net 4.0 is not able to timeout, so what is the correct way of doing that in .Net 4.0? .Net 4.0无法超时,那么在.Net 4.0中这样做的正确方法是什么?

FWIW, you can use async/await in 4.0 projects, just use the async targeting pack . FWIW,您可以在4.0项目中使用async / await,只需使用异步定位包即可 Works Great For Me! 适合我的作品!

Does this really have anything to do with async/await? 这真的与async / await有关吗? Looks like you're just needing a way to cancel the token, independently of async/await, right? 看起来你只需要一种取消令牌的方法,独立于异步/等待,对吗? In that case, could you simply create a Timer that calls Cancel after the timeout? 在这种情况下,您可以简单地创建一个在超时后调用取消的Timer吗?

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

EDIT 编辑

My initial response above is the basic idea, but a more robust solution is can be found in Is CancellationTokenSource.CancelAfter() leaky? 我上面的初步回答是基本的想法,但是在一个更好的解决方案可以在Is CancellationTokenSource.CancelAfter()漏洞中找到? (actually the .Net 4.5 implementation of the constructor you're seeking). (实际上是你正在寻求的构造函数的.Net 4.5实现)。 Here's a function you can use to create timeout tokens based on that code. 这是一个可用于根据该代码创建超时令牌的函数。

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

You can still use CancelAfter() , which is an extension method in Microsoft.Bcl.Async which is very similar to accepted answer above. 您仍然可以使用CancelAfter() ,它是Microsoft.Bcl.Async的扩展方法,与上面接受的答案非常相似。

This is the refereance souce code when I press F12 to see the implementation of CancelAfter() : 当我按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);
  }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM