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

动机

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

在Stephen Toub的异步方法,C#迭代器和任务中,我发现了一个可以很好地用于.NET 4.0的替代方法。 还有十几个其他实现甚至可以在.NET 2.0中使用该方法,尽管它们看起来很少过时且function较少。

所以现在我的.NET 4.0代码看起来像(注释部分显示它是如何在.NET 4.5中完成的):

//private async Task ProcessMessageAsync() private IEnumerable ProcessMessageAsync() { //var udpReceiveResult = await udpClient.ReceiveAsync(); var task = Task .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时 ,有一个构造函数将时间跨度作为超时参数,以便在指定的时间段内取消生成的CancellationTokenSource
.Net 4.0无法超时,那么在.Net 4.0中这样做的正确方法是什么?

这真的与async / await有关吗? 看起来你只需要一种取消令牌的方法,独立于异步/等待,对吧? 在这种情况下,你可以简单地创建一个在超时后调用取消的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; } 

FWIW,您可以在4.0项目中使用async / await,只需使用异步定位包即可 。 适合我的作品!

您仍然可以使用CancelAfter() ,它是Microsoft.Bcl.Async的扩展方法,与上面接受的答案非常相似。

当我按F12查看CancelAfter()的实现时,这是参考源代码:

  /// Cancels the  after the specified duration. /// The CancellationTokenSource. /// The due time in milliseconds for the source to be canceled. 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); }