Consider the following code. I am starting with a task that does nothing, and then using ContinueWith() to start 10 calls to a method that increments a counter.
When I run this program, it prints "0", indicating that the increment() method hasn't been called at all. I was expecting it to be called 10 times, since that's how many times I called ContinueWith().
If I uncomment the "Thread.Sleep(20)" line, then it prints "10" as expected.
This happens in either release or debug mode. My system is a core 2 quad with hyperthreading (8 logical cores) running Windows 7 x64.
I assume I have some kind of fundamental misunderstanding about how Task.ContinueWith() works....
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication4
{
class Program
{
static void Main()
{
using (var task = Task.Factory.StartNew(()=>{}))
{
for (int i = 0; i < 10; ++i)
{
task.ContinueWith(_=> increment());
// Thread.Sleep(20); // Uncomment to print 10 instead of 0.
}
task.Wait();
}
// This prints 0 UNLESS you uncomment the sleep above.
Console.WriteLine(counter);
}
static void increment()
{
Interlocked.Increment(ref counter);
}
private static int counter;
}
}
Can anyone shed any light on what's going on here?
ContinueWith(Action<Task,Object>, Object, TaskScheduler)Creates a continuation that receives caller-supplied state information and executes asynchronously when the target Task completes.
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.
The reason is simple: You wait on the task that is already finished. What you really want is to wait for the ten tasks you created in the loop:
var tasks = new List<Task>();
for (int i = 0; i < 10; ++i)
{
tasks.Add(task.ContinueWith(_=> increment()));
}
Task.WaitAll(tasks.ToArray());
Sure. You're printing out the value when the initial task has completed - you're not waiting for the continuation to occur. In other words, when the initial task completes, 11 things happen:
You could effectively chain all these together if you want:
task = task.ContinueWith(_=> increment());
Then when the original task finishes, one incrementer will fire. When that finishes, the next incrementer will fire. When that finishes [...]. And when the final incrementer has finished, your Wait
call will return.
If you change the main body as follows:
using (var task = Task.Factory.StartNew(() => { }))
{
var t = task;
for (int i = 0; i < 10; ++i)
{
t = t.ContinueWith(_ => increment());
// Thread.Sleep(20); // Uncomment to print 10 instead of 0.
}
t.Wait();
}
You will get 10
without sleeping. The main change is t = t.ContinueWith(...)
and t
is necessary because you're not allowed to change the using()
variable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With