我想安排一个任务以x ms开始,并且能够在它开始之前(或者只是在任务开始时)取消它。
第一次尝试就像是
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);
但我觉得应该有一个更好的方法,因为这会在睡眠中耗尽一个线程,在此期间它可以被取消。
我的其他选择是什么?
答案 0 :(得分:28)
与Damien_The_Unbeliever mentioned类似,异步CTP包含Task.Delay
。幸运的是,我们有Reflector:
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;
}
}
答案 1 :(得分:19)
由于.NET 4.5现已发布,因此有一种非常简单的内置方式来延迟任务:只需使用Task.Delay()
。在幕后,它使用ohadsc decompiled。
答案 2 :(得分:8)
将来的正确答案可能是Task.Delay
。但是,目前只能通过Async CTP(在CTP中,它在TaskEx而不是Task)上提供。
不幸的是,因为它仅在CTP中,所以没有很多很好的文档链接。
答案 3 :(得分:4)
查看"Parallel Programming with .NET 4 Samples"中的TaskFactoryExtensions_Delayed。
答案 4 :(得分:4)
我还没有对此进行过测试,但这是第一次使用包装器方法创建初始“延迟”任务或在延迟后继续。如果您发现问题,请随时纠正。
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);
}
答案 5 :(得分:3)
您可以使用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();
}