Idan
Idan

Reputation: 2879

Task.ContinueWith does not call 2nd task (only first one is running)

My application lets the user create "Task" objects from the UI and set their order of executions. From there, when the user click "run" all of the Tasks (by order) are sent to a class that suppose to chain and run them. The class is as follow:

public class MaintenanceTask
{
    private CancellationTokenSource m_CancellationTokenSource;
    private Task m_BatchTask;

    public MaintenanceTask()
    {            
        m_CancellationTokenSource = new CancellationTokenSource();            
    }

    public void AddTaskIdent(TaskIdentBase ident)
    {
        Task newTask = ident.Create(m_CancellationTokenSource.Token);

        if (m_BatchTask == null)
            m_BatchTask = newTask;            
        else                
            m_BatchTask.ContinueWith(prev=>newTask);                        
    }

    public void Run()
    {
        if (m_BatchTask != null)
            m_BatchTask.Start();
    }
}

The AddTaskIdent is recieving a base class that knows how to create the Task object. My problem is that calling Run() start the first task that was sent to the class. It runs it until finished by no other task following it gets to run. It's just stops after the first task is done.

Am I missing something with the ContinueWith mechanism ?

Upvotes: 2

Views: 720

Answers (5)

NSFW
NSFW

Reputation: 557

Note that "foo.ContinueWith(completedTask => otherTask);" does not start otherTask. You need something like "foo.ContinueWith(completedTask => otherTask.Start());"

Try this in a debugger, that may help you see how this works:

        static void Main(string[] args)
        {
            Task task1 = new Task(() => Console.WriteLine("1"));
            Task task2 = new Task(() => Console.WriteLine("2"));
            Task task3 = new Task(() => Console.WriteLine("3"));

            task1.ContinueWith(previous => task2.Start());
            task2.ContinueWith(previous => task3.Start());

            task1.Start();
            task2.Wait();
            Console.WriteLine("Done.");
        }

Upvotes: 1

Idan
Idan

Reputation: 2879

Finally got it to work after merging data from all answers and continuing googling. The working class is:

public class MaintenanceTask
{
    private CancellationTokenSource m_CancellationTokenSource;
    private Task m_FirstTask;
    private Task m_LastTask;

    public MaintenanceTask()
    {
        m_CancellationTokenSource = new CancellationTokenSource();
    }

    public void AddTaskIdent(TaskIdentBase ident)
    {
        Task newTask = ident.Create(m_CancellationTokenSource.Token);

        if (m_FirstTask == null)
        {
            m_FirstTask = newTask;
            m_LastTask = m_FirstTask;
        }
        else
        {                
            m_LastTask.ContinueWith(prev => newTask.Start());
            m_LastTask = newTask;
        }
    }

    public void Run()
    {
        if (m_FirstTask != null)
            m_FirstTask.Start();
    }
}

Upvotes: 1

galenus
galenus

Reputation: 2127

The problem is in this line:

m_BatchTask.ContinueWith(prev=>newTask);

I presume the newTask is not started when it's being returned from ident.Create. So you should:

if (m_BatchTask != null)
{
    m_BatchTask = newTask;
    //you'd like to have the m_lastTask field because next chaining operation
    //should be on the last task, not the one you are starting
    m_lastTask = newTask;
}
else
{
    m_lastTask.ContinueWith(
        prev =>
        {
            newTask.Start();
            //doesn't matter to return newTask because in that case
            //on second chaining already you will get Task<Task>
            return (object)null; 
        });
    m_lastTask = newTask;
}

Upvotes: 1

Aron
Aron

Reputation: 15772

To be honest I think you are using Task incorrectly. In most cases Task represents work that is happening already.

This is how I would go about writing the code you "want" assuming C#5.

public void AddToQueue(Action action)
{
    if (m_BatchTask == null)
        m_BatchTask = Task.Run(action);            
    else                
        m_BatchTask = AddToQueue(m_BatchTask, action);                        
}

public async Task AddToQueue(Task existingTask, Action action)
{
    await existingTask;
    action();                       
}

Upvotes: 0

Boas Enkler
Boas Enkler

Reputation: 12557

Have you checked that the else branch with

  else                
        m_BatchTask.ContinueWith(prev=>newTask);       

is called?

As I see m_Batch task is initially null when he applies next task to it but does not raise the continue with

Upvotes: 0

Related Questions