Question

I have some GUI on a bunch of LINQ queries. The queries take some time to execute, so I would like for the GUI to be responsive and show busyindicators and progress bars. Many of the queries are to check for certain conditions existing in the data. If the query returns an empty result, the app shall continue with the next query. If it returns a result, the return set will either be of severity "warnings" or "errors". If it is warnings, execution shall continue. If it is errors, it shall stop.

Much code plays "ping pong" with the threadpool and GUI. Quasi code:

TaskFactory.StartNew(()=>
    {
       Run in background
    }.ContinueInGui(()=>
    {
       Update something
    }).ContinueInBackground(()=>
    {
      Do more work;
    }).ContinueInGui(()=> etc etc

This is tidy and nice. However, I don't see how I can insert conditions to go different continuation routes or break off the continuation chain if errors are found in the data.

There is no method for ContinueWithIf( predicate ,delegate{},TaskScheduler) Do I use TaskCancellation, do I throw an exception? Or is there some simple branching mechanism that I'm not thinking of?

Was it helpful?

Solution

A good option here would be to use a CancelationTokenSource, and just mark it canceled if you want to "break" your continuation chain. By including TaskContinuationOptions.NotOnCanceled in the ContinueWith for subsequent tasks, you can have them not get scheduled at any point by marking a CancelationTokenSource as canceled.

If you really want to use a predicate, instead of setting up the continuation in the main method, you'd need to make a custom method to handle this for you. This can be done by having an extension method that attaches a continuation - that continuation can check the predicate, and fire off the continuation if appropriate. This would look something like:

public static Task ContinueWithIf(this Task task, Func<bool> predicate, Action<Task> continuation, TaskScheduler scheduler)
{
    var tcs = new TaskCompletionSource<object>(); 

    task.ContinueWith( t =>
    {
        if (predicate())
        {
            new TaskFactory(scheduler).StartNew( 
                () => 
                {
                    continuation(task); 
                    tcs.SetResult(null); 
                });
        }
        else
        {
            tcs.TrySetCanceled();
        }
    });

    return tcs.Task;
}

Granted, you'd probably want to make a version for Task<T> in addition, as well as handle the faulted/canceled states on the Task. That being said, it should function correctly.

OTHER TIPS

If there are errors, you should consider making your task fault accordingly. Then you can use TaskContinuationOptions.OnlyOnRanToCompletion etc in the ContinueWith call.

Basically there are three possible states at the end of a task's life:

  • RanToCompletion
  • Canceled
  • Faulted

You can make ContinueWith apply to any sensible combination of those statuses, and you can attach different continuations to the same parent task if you want to do different things based on error vs success vs cancellation etc.

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