正确的方式推迟任务的开始
我想安排一个任务,以x毫秒开始,并能够在开始之前取消它(或只是在任务开始时)。
第一次尝试会是这样的
var _cancelationTokenSource = new CancellationTokenSource(); var token = _cancelationTokenSource.Token; Task.Factory.StartNew(() => { token.ThrowIfCancellationRequested(); Thread.Sleep(100); token.ThrowIfCancellationRequested(); }).ContinueWith(t => { token.ThrowIfCancellationRequested(); DoWork(); token.ThrowIfCancellationRequested(); }, token);
但我觉得应该有一个更好的办法,因为这会在睡眠中用掉一根线,在此期间它可能被取消。
我的其他select是什么?
像Damien_The_Unbeliever提到的 ,Async CTP包含Task.Delay
。 幸运的是,我们有reflection器:
public static class TaskEx { static readonly Task _sPreCompletedTask = GetCompletedTask(); static readonly Task _sPreCanceledTask = GetPreCanceledTask(); public static Task Delay(int dueTimeMs, CancellationToken cancellationToken) { if (dueTimeMs < -1) throw new ArgumentOutOfRangeException("dueTimeMs", "Invalid due time"); if (cancellationToken.IsCancellationRequested) return _sPreCanceledTask; if (dueTimeMs == 0) return _sPreCompletedTask; var tcs = new TaskCompletionSource<object>(); var ctr = new CancellationTokenRegistration(); var timer = new Timer(delegate(object self) { ctr.Dispose(); ((Timer)self).Dispose(); tcs.TrySetResult(null); }); if (cancellationToken.CanBeCanceled) ctr = cancellationToken.Register(delegate { timer.Dispose(); tcs.TrySetCanceled(); }); timer.Change(dueTimeMs, -1); return tcs.Task; } private static Task GetPreCanceledTask() { var source = new TaskCompletionSource<object>(); source.TrySetCanceled(); return source.Task; } private static Task GetCompletedTask() { var source = new TaskCompletionSource<object>(); source.TrySetResult(null); return source.Task; } }
由于.NET 4.5已经发布,所以有一个非常简单的内置方式来延迟任务:只需使用Task.Delay()
。 在幕后,它使用了ohadsc反编译的实现。
将来的正确答案可能是Task.Delay
。 但是,目前只有通过Async CTP才能使用(而且在CTP中,它在TaskEx上而不是在Task上)。
不幸的是,因为只有CTP,所以没有太多的文档链接。
查看“使用.NET 4示例进行并行编程”中的TaskFactoryExtensions_Delayed。
我没有testing过这个,但是这里是第一遍的包装方法来创build一个初始的“延迟”任务或延迟后继续。 如果您发现问题,请随时纠正。
public static Task StartDelayTask(int delay, CancellationToken token) { var source = new TaskCompletionSource<Object>(); Timer timer = null; timer = new Timer(s => { source.TrySetResult(null); timer.Dispose(); }, null, delay, -1); token.Register(() => source.TrySetCanceled()); return source.Task; } public static Task ContinueAfterDelay (this Task task, int delay, Action<Task> continuation, CancellationToken token) { var source = new TaskCompletionSource<Object>(); Timer timer = null; var startTimer = new Action<Task>(t => { timer = new Timer(s => { source.TrySetResult(null); timer.Dispose(); },null,delay,-1); }); task.ContinueWith (startTimer, token, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Current); token.Register(() => source.TrySetCanceled()); return source.Task.ContinueWith(continuation, token); }
您可以使用Token.WaitHandle.WaitOne(int32毫秒)重载方法来指定等待您的任务的毫秒数。 但Thread.Sleep(xxx)和Token.WaitHandle.WaitOne(xxx)之间的主要区别是稍后会阻塞线程,直到指定的时间过去或令牌已被取消。
这是一个例子
void Main() { var tokenSource = new CancellationTokenSource(); var token = tokenSource.Token; var task = Task.Factory.StartNew(() => { // wait for 5 seconds or user hit Enter key cancel the task token.WaitHandle.WaitOne(5000); token.ThrowIfCancellationRequested(); Console.WriteLine("Task started its work"); }); Console.WriteLine("Press 'Enter' key to cancel your task"); Console.Read(); tokenSource.Cancel(); }