Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper way to chain Tasks

I want to chain Tasks, then start the chain in parallel. This snippet is just to illustrate my question:

        var taskOrig = new Task(() => { });
        var task = taskOrig;
        foreach (var msg in messages)
        {
            task=task.ContinueWith(t => Console.WriteLine(msg));
        }
        taskOrig.Start();

Everything works fine except a little perfectionist inside me doesn't like having empty method executed first () => { }.

Is there any way to avoid it?

I do understand It's barely affecting performance (unless you do it really often), but still. Performance matters in my case, so checking if task exists in every iteration is not the way to do it.

like image 338
Anri Avatar asked Nov 26 '12 16:11

Anri


People also ask

What is a task chain?

Task Chains is a feature found in the Metasploit Pro version which helps us to schedule tasks and execute them. It is generally used for processes that run periodically, for example, network scanning. To configure a task, let's go to Tasks → Chains → New Task Chain.

What is a continuation task?

A continuation task (also known just as a continuation) is an asynchronous task that's invoked by another task, known as the antecedent, when the antecedent finishes.

What is the difference between task run and task factory StartNew?

Task. Run(action) internally uses the default TaskScheduler , which means it always offloads a task to the thread pool. StartNew(action) , on the other hand, uses the scheduler of the current thread which may not use thread pool at all!

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.


3 Answers

You could do this:

Task task = Task.FromResult<object>(null);
foreach (var msg in messages)
{
    task = task.ContinueWith(t => Console.WriteLine(msg));
}

The previous solution won't work in 4.0. In 4.0 you'd need to do the following instead:

var tcs = new TaskCompletionSource<object>();
Task task = tcs.Task;
foreach (var msg in messages)
{
    task = task.ContinueWith(t => Console.WriteLine(msg));
}

tcs.SetResult(null);

(You can move SetResult to before the foreach loop if you prefer.)

Technically it's not the same as the continuations will start executing while you're still adding more. That's unlikely to be a problem though.

Another option would be to use something like this:

public static Task ForEachAsync<T>(IEnumerable<T> items, Action<T> action)
{
    return Task.Factory.StartNew(() =>
    {
        foreach (T item in items)
        {
            action(item);
        }
    });
}

An example usage would be:

ForEachAsync(messages, msg => Console.WriteLine(msg));
like image 50
Servy Avatar answered Sep 28 '22 13:09

Servy


One way to do that, is to create task in loop if it is null, but the code you provide looks better for me:

Task task = null;
foreach (var msg in messages)
{   
    if (task == null)
      task = new Task(() => Console.WriteLine(msg))
    else
      task = task.ContinueWith(t => Console.WriteLine(msg));
}
task.Start();
like image 23
Arsen Mkrtchyan Avatar answered Sep 28 '22 14:09

Arsen Mkrtchyan


Perhaps this:

if(messages.Length > 0)
{
    Task task = new Task(t => Console.WriteLine(messages[0]));

    for(int i = 1; i < messages.Length; i++)
    {
        task = task.ContinueWith(t => Console.WriteLine(messages[i]));
    }
    task.Start();
}
like image 45
Stephen Gilboy Avatar answered Sep 28 '22 14:09

Stephen Gilboy