Question

I'm attempting to use the .NET Task framework. My scenario is this: I have a task that needs to execute whenever any properties on my class change. If it was already executing, the task should bail immediately and restart such that there is only one task running at any given time. In reality, it may take a second to stop the task during which time I may receive multiple PropertyChanged events (that should only trigger one rerun of the task). And my PropertyChanged handler cannot block.

With that scenario, it seems I need a volatile flag to make the previous task exit. However, I don't know where to wait for that to exit. At the same time, I don't think I can add a ContinueWith method when the thing is not running. (And I don't want to nest ContinueWith multiple levels deep, do I?)

Is there any help for me in the Task library? What other recommendations do you have for this one-level-deep queue of tasks? I'm using .NET 4.5.

Was it helpful?

Solution

So I wanted to write a TaskScheduler for this, but that interface doesn't provide a mechanism for ensuring that the tasks all have a cancellation token and being able to cancel an already running tasks, so we'll have to make due with a scheduler that doesn't follow a provided TPL model/interface.

When scheduling a first cancels the previous task (which won't do anything if there was no previous task or it has since finished). Next it sets the current task to be a task that isn't started until the previous task ends, and in its body runs the given action. A lock is needed so that if there are two calls to Schedule one will immediately cancel the other, and the non-cancelled version will be the one assigned to current.

public class SingleThreadedKillPredecssorScheduler
{
    private Task current = Task.FromResult(true);
    private CancellationTokenSource cts = new CancellationTokenSource();
    private object key = new object();

    public Task Schedule(Action<CancellationToken> action)
    {
        lock (key)
        {
            cts.Cancel();
            cts = new CancellationTokenSource();
            current = current.ContinueWith(t => action(cts.Token), cts.Token);
            return current;
        }
    }
    public Task<T> Schedule<T>(Func<CancellationToken, T> function)
    {
        lock (key)
        {
            cts.Cancel();
            cts = new CancellationTokenSource();
            var next = current.ContinueWith(t => function(cts.Token), cts.Token);
            current = next;
            return next;
        }
    }
}

OTHER TIPS

The simplest way I can think of would be to lock some object at the start of the task and keep it locked during execution of the Task. That way a new Task will start, but won't do anything until the previous Task is finished.

Also, I don't think that there is a way to stop Task, other to notify it and check within the Task whether such notification exists and exit the Task then. You can use CancellationToken, or even just bool if case is simple, I would use it in this case.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top