First of all I will explain what I am trying to do.
I have a component A which is using a component B.
In order to communicate between both of them, I need to use event.
One of my prerequisites here, is to let component B running asynchronously AND to run event handler in sequential order they've been called.
Besides, I would like to cancel the pipe of call (when user ask it). Thus all event handler called which are not executed yet will never do.
Solution to achieve on is TPL. I made a POC of what I'm trying to do :
static void Main(string[] args)
{
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
var t = Task.Factory.StartNew(() => DoSomeWork(token));
//.ContinueWith((prevTask) => DoSomeWork(token));
t.ContinueWith((prevTask) => DoSomeWork(token));
Task.WaitAll(t);
Console.WriteLine("Finish");
Console.ReadKey();
}
static int id = 1;
static void DoSomeWork(CancellationToken ct)
{
ct.ThrowIfCancellationRequested();
Thread.Sleep(1000);
Console.WriteLine(id++);
}
There is the output of this snippet :
1
Finish
2
As you can see, it finishes before it really does. It displays 2 after Finish.
If I modify the previous code by this, it works :
static void Main(string[] args)
{
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
var t = Task.Factory.StartNew(() => DoSomeWork(token))
.ContinueWith((prevTask) => DoSomeWork(token));
//t.ContinueWith((prevTask) => DoSomeWork(token));
Task.WaitAll(t);
Console.WriteLine("Finish");
Console.ReadKey();
}
static int id = 1;
static void DoSomeWork(CancellationToken ct)
{
ct.ThrowIfCancellationRequested();
Thread.Sleep(1000);
Console.WriteLine(id++);
}
There is the output of this snippet :
1
2
Finish
As you understand, I do not need to use the continueWith statement in task declaration, but when an event is raised.
Why Task.WaitAll(t); doesn't work one the first sample ?
Is anybody can help me ?
The initial problem is that you are creating two tasks but waiting only on one.
// t is the "first" task
var t = Task.Factory.StartNew(() => DoSomeWork(token));
// the continuation task is not assigned
t.ContinueWith((prevTask) => DoSomeWork(token));
Task.WaitAll(t); // <-- wait only on "t", which is the first task
Console.WriteLine("Finish"); // when the first task finishes, this gets printed
// now the continuation task is executing, but you are not waiting for it
What happens on the second snippet is that you are waiting on the continuation task, so it'll wait until it has finished
// t is now the continuation task
var t = Task.Factory.StartNew(() => DoSomeWork(token))
.ContinueWith((prevTask) => DoSomeWork(token));
Task.WaitAll(t); // <-- wait till the continuation task has finished
So, the second method is ok, but if you want a finer control, just assign a task variable to wait on the continuation task:
// t is the "first" task
var t = Task.Factory.StartNew(() => DoSomeWork(token));
// The continuation task is assigned to "t2"
var t2 = t.ContinueWith((prevTask) => DoSomeWork(token));
Task.WaitAll(new [] { t, t2 } ); // <-- wait for all tasks
Console.WriteLine("Finish");
Note: I've followed your code for the examples, but WaitAll
doesn't take a single task as a parameter (it takes an array of tasks), so that probably doesn't compile. You either use Task.Wait
or pass an array to WaitAll