取消.Net 4.0中的任务延迟

我目前正在尝试在必须以.Net 4.0为目标的程序中实现.Net 4.5的Task.Delay()方法的替代品。 我在这个博客上找到了以下代码。

  /* You can write Task-based asynchronous methods by utilizing a TaskCompletionSource. A TaskCompletionSource gives you a 'slave' Task that you can manually signal. Calling SetResult() signals the task as complete, and any continuations kick off. */ void Main() { for (int i = 0; i  "Done".Dump()); } } Task Delay (int milliseconds) // Asynchronous NON-BLOCKING method { var tcs = new TaskCompletionSource(); new Timer (_ => tcs.SetResult (null)).Change (milliseconds, -1); return tcs.Task; } 

Tasks对我来说相当新鲜。 System.Threading.TimerTaskCompletionSource对我来说是全新的(截至今天),我正在与他们挣扎。 除此之外,我想知道如何在此代码中添加CancellationTokenfunction。 我假设我可以像这样在Delay()方法中添加一个参数:

 Task Delay (int milliseconds, CancellationToken token) // Asynchronous NON-BLOCKING method { var tcs = new TaskCompletionSource(); new Timer (_ => tcs.SetResult (null)).Change (milliseconds, -1); return tcs.Task; } 

…但是,我在哪里放置检查令牌和退出方法的逻辑? 在回调的某个地方? 这有可能吗?

我试图尽可能少地更改你的代码,但这是一个工作示例,其行为与Task.Delay相同。

重要的是要注意我使用TrySetCanceledTrySetResult因为Timer可以在任务取消后完成。 理想情况下,您想要停止计时器。

另请注意,取消的任务将抛出TaskCanceledException

 static void Main(string[] args) { // A cancellation source that will cancel itself after 1 second var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(1)); try { // This will only wait 1 second because as it will be cancelled. Task t = Delay(5000, cancellationTokenSource.Token); t.Wait(); Console.WriteLine("The task completed"); } catch (AggregateException exception) { // Expecting a TaskCanceledException foreach (Exception ex in exception.InnerExceptions) Console.WriteLine("Exception: {0}", ex.Message); } Console.WriteLine("Done"); Console.ReadLine(); } private static Task Delay(int milliseconds, CancellationToken token) { var tcs = new TaskCompletionSource(); token.Register(() => tcs.TrySetCanceled()); Timer timer = new Timer(_ => tcs.TrySetResult(null)); timer.Change(milliseconds, -1); return tcs.Task; } 

再读一下你的问题。 如果您需要Task.Delay并且您的目标是.NET 4.0,那么您应该使用来自http://www.nuget.org/packages/Microsoft.Bcl.Async/的Microsoft Async nuget包它包含方法TaskEx.Delay

像这样 :

 token.Register(() => tcs.TrySetCancelled()); 

在这里,您是一个防止垃圾收集器处理计时器的版本

  public static Task Delay(int milliseconds, CancellationToken token) { var tcs = new TaskCompletionSource(); var timer = new OneShotTimer((t) => { using ((OneShotTimer)t) tcs.SetResult(null); }); token.Register(() => { if (timer.TryCancel()) { using (timer) tcs.SetCanceled(); } }); timer.Start(milliseconds); return tcs.Task; } public class OneShotTimer : IDisposable { private readonly object sync = new object(); private readonly TimerCallback oneShotCallback; private readonly Timer timer; private bool isActive; public OneShotTimer(TimerCallback oneShotCallback, int dueTime = Timeout.Infinite) { this.oneShotCallback = oneShotCallback; this.isActive = dueTime != Timeout.Infinite; this.timer = new Timer(callback, this, dueTime, Timeout.Infinite); } public void Dispose() { timer.Dispose(); } public void Start(int dueTime) { if (!tryChange(true, dueTime)) throw new InvalidOperationException("The timer has already been started"); } public bool TryCancel() { return tryChange(false, Timeout.Infinite); } public bool tryChange(bool targetIsActive, int dueTime) { bool result = false; lock (sync) { if (isActive != targetIsActive) { result = true; isActive = targetIsActive; timer.Change(dueTime, Timeout.Infinite); } } return result; } private static void callback(object state) { var oneShotTimer = (OneShotTimer)state; if (oneShotTimer.TryCancel()) oneShotTimer.oneShotCallback(oneShotTimer); } }