Maybe my brain is a bit fried so I'm missing some nice way to do the following... I want to be able to launch a timer though a Task that runs on a certain interval and checks some co开发者_StackOverflow社区ndition on each interval whether it should cancel itself, what's the most elegant solution?
Optimally I'd like something like:
Task.Factory.StartNew(() =>
{
Timer.Do(TimeSpan.FromMilliSeconds(200),() => ShouldCancel(), ()=>
{
//DoStuff
});
});
using a while/thread-sleep loop doesn't seem optimal. I guess I could define and use a ordinary timer but it seems a bit clunky...
How about something like the following.I'm sure the API could be cleaned up a bit.
Points to note:
The DoWork method must support cooperative cancellation, this is the only cancellation approach supported by the Task Parallel Library.
The timer must start inside the Task, otherwise the Task may be created and scheduled but not executed and the timer will be timing task wait time not execution time.
If you want to provide other external mechanisms for cancellation (other tokens) then you need to pass in another context and link them. See: CancellationTokenSource.CreateLinkedTokenSource
This is only approximate as System.Threading.Timer only has millisecond accuracy. It should be good enough for limiting a Task to run for a few seconds.
public static class TimeLimitedTaskFactory
{
public static Task StartNew<T>
(Action<CancellationToken> action, int maxTime)
{
Task tsk = Task.Factory.StartNew(() =>
{
var cts = new CancellationTokenSource();
System.Threading.Timer timer = new System.Threading.Timer(o =>
{
cts.Cancel();
Console.WriteLine("Cancelled!");
}, null, maxTime, int.MaxValue);
action(cts.Token);
});
return tsk;
}
}
class Program
{
static void Main(string[] args)
{
int maxTime = 2000;
int maxWork = 10;
Task tsk = TimeLimitedTaskFactory
.StartNew<int>((ctx) => DoWork(ctx, maxWork), maxTime);
Console.WriteLine("Waiting on Task...");
tsk.Wait();
Console.WriteLine("Finished...");
Console.ReadKey();
}
static void DoWork(CancellationToken ctx, int workSize)
{
int i = 0;
while (!ctx.IsCancellationRequested && i < workSize)
{
Thread.Sleep(500);
Console.WriteLine(" Working on ", ++i);
}
}
}
You also can use RX library.
var timerTask = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(3));
timerTask.Subscribe(x =>
{
//Do stuff here
});
I think this is what you want:
var cancelToken = new CancellationTokenSource();
var tt = Task.Factory.StartNew(obj =>
{
var tk = (CancellationTokenSource) obj;
while (!tk.IsCancellationRequested)
{
if (condition)//your condition
{
//Do work
}
Thread.Sleep(1000);
}
}, cancelToken);
精彩评论