Reputation: 2879
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
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
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
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
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
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