Task.ContinueWith not working how I expected
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 t开发者_如何学Cask = 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?
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:
- 10 new tasks start executing, each incrementing the counter
- You print out the counter
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.
精彩评论