C#5.0异步/等待构造非常棒,但遗憾的是,微软只展示了.NET 4.5和VS 2012的候选版本,这些技术将在我们的项目中得到广泛采用需要一些时间。
在Stephen Toub的Asynchronous methods, C# iterators, and Tasks我找到了一个可以很好地用于.NET 4.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时,有一个构造函数将时间跨度作为超时参数,因此生成的CancellationTokenSource
会在指定的时间段内取消。
.Net 4.0无法超时,那么在.Net 4.0中这样做的正确方法是什么?
答案 0 :(得分:5)
FWIW,您可以在4.0项目中使用async / await,只需使用the async targeting pack即可。适合我的作品!
答案 1 :(得分:4)
这与async / await有什么关系吗?看起来你只需要一种取消令牌的方法,独立于异步/等待,对吗?在这种情况下,你可以简单地创建一个在超时后调用取消的Timer吗?
new Timer(state => cts.Cancel(), null, BootstrapResponseTimeout, Timeout.Infinite);
修改强>
我上面的初步回答是基本的想法,但是在Is CancellationTokenSource.CancelAfter() leaky?(实际上是你正在寻求的构造函数的.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;
}
答案 2 :(得分: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);
}