咨询区
-
Bruno Lopes
我想实现一个可以在延迟 Xms
后执行一个 task 的需求,并要求能够实现在 xms
之前取消 task 的执行,我的参考代码如下:
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);
虽然用 Sleep + ContinueWith
的方式可以实现,但我觉得还不够完美,请问是否有更好的办法?
-
valentasm
你可以用 WaitOne
的另一个重载方法: Token.WaitHandle.WaitOne(int32 milliseconds)
, 它比Thread.Sleep(xxx)
🐂👃的地方在于,可以实现让当前的线程等待xxx秒同时也可以在 xxxs 内被取消。
参考如下代码:
static void Main(string[] args)
{
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();
}
-
jyoung
如果你的程序是 .NET 4.5
以上或者 .netcore
的话,可以使用 Task.Delay()
来实现,非常方便,参考如下代码:
static void Main(string[] args)
{
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
tokenSource.Cancel();
Task.Delay(1000, token).ContinueWith(t =>
{
Console.WriteLine("hello world!");
},token);
Console.ReadLine();
}
-
Dan Bryant
你可以在底层用 timer 作为调度机制,下面是我的完整代码实现。
public static Task StartDelayTask(int delay, CancellationToken token)
{
var source = new TaskCompletionSource();
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 continuation,
CancellationToken token)
{
var source = new TaskCompletionSource();
Timer timer = null;
var startTimer = new Action(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);
}
点评区
三种方式都很好,不过我个人更倾向于用 Task.Delay()
,学习了。