mshsayem
mshsayem

Reputation: 18018

Why these tasks are starting delayed?

I am trying this code (just spawn some tasks and simulate work):

var tasks = Enumerable.Range(1, 10).Select(d => Task.Factory.StartNew(() =>
{
    Console.Out.WriteLine("Processing [{0}]", d);
    Task.Delay(20000).Wait(); // Simulate work. Here will be some web service calls taking 7/8+ seconds.
    Console.Out.WriteLine("Task Complete [{0}]", d);
    return (2 * d).ToString();
})).ToList();

var results = Task.WhenAll(tasks).Result;
Console.Out.WriteLine("All processing were complete with results: {0}", string.Join("|", results));

I was expecting to see 10 Processing ... in the console at once; but when I run, initially I see this output

Processing [1]
Processing [2]
Processing [3]
Processing [4]

Then 1/2 seconds later Processing [5], Processing [6] and others are shown slowly one after another.

Can you explain this? Does this mean tasks are being started as delayed? Why?

Upvotes: 1

Views: 137

Answers (2)

Ian Ringrose
Ian Ringrose

Reputation: 51927

I expect you have 4 cpu cores.

Having two (cpu bond) threads fighting over a core leads to it taking longer to complete the work then have 1 thread doing the first task, then doing the second task run.

Until it know otherwise the task system assumes tasks are short running and CPU bound and that they will use “none blocking” IO.

Therefore I expect that the task systems defaults to the number of threads being close to the number of cores.

Using TaskCreationOptions.LongRunning

provides a hint to the TaskScheduler that oversubscription may be warranted. Oversubscription lets you create more threads than the available number of hardware threads.

And lastly tasks are not threads, they are design to hide a lot of the details of threads from you, including controlling the number of threads that are in use. It is reasonable to create 100s of tasks, if you created 100s of threads all trying to run at the same time, the cpu cache etc will have a very hard time.


However lets get back to what you are trying to do. Your example simulates CPU bound work. You say your tasks will be making calls to a web service - meaning they will be IO bound.

As such, they should be running asynchronously. However, Task.Delay(20000).Wait(); waits synchronously, so it doesn't represent what will/should actually be going on. See Gediminas Masaitis answer for a code sample using await to make the delay asynchronously. However as soon as you use yet more asynchronously code, you need to think more about locking etc.

Asynchronously IO is clearly better if you have 100s of requests going on at the same time. However if you just have a "hand full" and no other usage of await in your application then TaskCreationOptions.LongRunning may be good enough.

Upvotes: 1

Gediminas Masaitis
Gediminas Masaitis

Reputation: 3212

As mentioned in another answer, using TaskCreationOptions.LongRunning will solve your problem.

But this is not how you should approach your problem. Your example simulates CPU bound work. You say your tasks will be making calls to a web service - meaning they will be IO bound.

As such, they should be running asynchronously. However, Task.Delay(20000).Wait(); waits synchronously, so it doesn't represent what will/should actually be going on.

Take this example instead:

var tasks = Enumerable.Range(1, 10).Select(async d =>
{
    Console.Out.WriteLine("Processing [{0}]", d);
    await Task.Delay(5000); // Simulate IO work. Here will be some web service calls taking 7/8+ seconds.
    Console.Out.WriteLine("Task Complete [{0}]", d);
    return (2*d).ToString();
}).ToList();
var results = Task.WhenAll(tasks).Result;
Console.Out.WriteLine("All processing were complete with results: {0}", string.Join("|", results));

All tasks start instantly as expected.

Upvotes: 3

Related Questions