Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I schedule a conditional ContinueWith

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?

like image 418
Tormod Avatar asked Oct 24 '11 08:10

Tormod


People also ask

What does calling Task ContinueWith() do?

ContinueWith(Action<Task,Object>, Object, TaskScheduler)Creates a continuation that receives caller-supplied state information and executes asynchronously when the target Task completes.

What is ContinueWith c#?

The ContinueWith function is a method available on the task that allows executing code after the task has finished execution. In simple words it allows continuation. Things to note here is that ContinueWith also returns one Task. That means you can attach ContinueWith one task returned by this method.

How do I run a Task in C#?

To start a task in C#, follow any of the below given ways. Use a delegate to start a task. Task t = new Task(delegate { PrintMessage(); }); t. Start();


2 Answers

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.

like image 101
Reed Copsey Avatar answered Oct 07 '22 12:10

Reed Copsey


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.

like image 25
Jon Skeet Avatar answered Oct 07 '22 10:10

Jon Skeet