I am trying to create a TaskScheduler that limits the number of threads that can be run at the same time.
You might want to skip straight to the answer. ;)
var scheduler = new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, 5)
.ConcurrentScheduler;
What I'm not getting is what the await on DoAdditionalWorkAsync() is doing with the concurrency.
Task schedulers only apply to executing code. When an async
method executes on a task scheduler, you can think of it as being broken up into multiple tasks, with a break at each await
point. By default, after the await
, the async
method will re-enter its task scheduler. The async
method is not "in" the task scheduler while it is await
ing.
So, the scheduling limitation (5 at a time) simply doesn't apply while the method is await
ing. So, in your DoWork
, the method will first increment the variable, then yield to the task scheduler. While yielded, it doesn't "count" towards your concurrency limitation. Later, when that method resumes, it will block the thread (which does "count") and increment the second variable.
With this code:
private static void Main(string[] args)
{
var scheduler = new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, 5)
.ConcurrentScheduler;
TaskFactory factory = new TaskFactory(scheduler);
for (int i = 0; i < 10; ++i)
{
factory.StartNew(() => DoWork());
}
Console.ReadKey();
}
private static int StaticIntValueWorkOne, StaticIntValueWorkTwo;
private static async Task DoWork()
{
// Do some stuff here like
Console.WriteLine(DateTime.UtcNow + " StaticIntValueWorkOne" + Interlocked.Increment(ref StaticIntValueWorkOne));
// And then more stuff that is async here
await Task.Yield();
Thread.Sleep(10000);
Console.WriteLine(DateTime.UtcNow + " StaticIntValueWorkTwo" + Interlocked.Increment(ref StaticIntValueWorkTwo));
}
I get this (expected) output:
3/20/2015 11:01:53 AM StaticIntValueWorkOne1
3/20/2015 11:01:53 AM StaticIntValueWorkOne5
3/20/2015 11:01:53 AM StaticIntValueWorkOne4
3/20/2015 11:01:53 AM StaticIntValueWorkOne2
3/20/2015 11:01:53 AM StaticIntValueWorkOne3
3/20/2015 11:01:53 AM StaticIntValueWorkOne6
3/20/2015 11:01:53 AM StaticIntValueWorkOne9
3/20/2015 11:01:53 AM StaticIntValueWorkOne10
3/20/2015 11:01:53 AM StaticIntValueWorkOne7
3/20/2015 11:01:53 AM StaticIntValueWorkOne8
3/20/2015 11:02:03 AM StaticIntValueWorkTwo1
3/20/2015 11:02:03 AM StaticIntValueWorkTwo3
3/20/2015 11:02:03 AM StaticIntValueWorkTwo2
3/20/2015 11:02:03 AM StaticIntValueWorkTwo4
3/20/2015 11:02:03 AM StaticIntValueWorkTwo5
3/20/2015 11:02:13 AM StaticIntValueWorkTwo6
3/20/2015 11:02:13 AM StaticIntValueWorkTwo7
3/20/2015 11:02:13 AM StaticIntValueWorkTwo8
3/20/2015 11:02:13 AM StaticIntValueWorkTwo9
3/20/2015 11:02:13 AM StaticIntValueWorkTwo10
If you want to limit the concurrency of asynchronous code, look into SemaphoreSlim
or TPL Dataflow.