I am trying to use the following custom task scheduler to limit concurrent web requests:
I'm using it like this:
const int CONCURRENCY_LEVEL = 2;
static void Main()
{
TaskFactory factory = new TaskFactory(new LimitedConcurrencyLevelTaskScheduler(CONCURRENCY_LEVEL));
Task.WaitAll(new[] { factory.StartNew(() => SomeAction("first")), factory.StartNew(() => SomeAction("second")) });
Console.WriteLine("All tasks ended");
Console.Read();
}
static void SomeAction(string task)
{
Console.WriteLine(task + " task started");
Console.WriteLine(AnotherTask(task).Result);
Console.WriteLine(task + " task ended");
}
static async Task<string> AnotherTask(string task)
{
return await Task.Run(() =>
{
Console.WriteLine(task + " inner task started");
// imitate web requests
Thread.Sleep(200);
Console.WriteLine(task + " inner task ended");
return "ok";
});
}
The problem is that the inner tasks are using my LimitedConcurrencyLevelTaskScheduler
instead of the default TaskScheduler
. This leads to self blocking and the output is like this:
second task started
first task started
first inner task started
second inner task started
second inner task ended
first inner task ended
When I change the CONCURRENCY_LEVEL
to 3
or change the factory
to Task.Factory
then obviously everything is working fine and the output is like:
first task started
second task started
first inner task started
second inner task started
second inner task ended
first inner task ended
ok
second task ended
ok
first task ended
All tasks ended
I cannot change the code in SomeAction
. What else can I do? Is there a bug in the Microsoft sample?
Specify TaskCreationOptions.HideScheduler
when starting tasks from your custom task scheduler:
Task.WaitAll(new[]
{
factory.StartNew(() => SomeAction("first"), TaskCreationOptions.HideScheduler),
factory.StartNew(() => SomeAction("second"), TaskCreationOptions.HideScheduler)
});
From MSDN:
HideScheduler
Prevents the ambient scheduler from being seen as the current scheduler in the created task. This means that operations likeStartNew
orContinueWith
that are performed in the created task will seeDefault
as the current scheduler.
Try
static async Task<string> AnotherTask(string task)
{
return await Task.Run(() =>
{
Console.WriteLine(task + " inner task started");
// imitate web requests
Thread.Sleep(200);
Console.WriteLine(task + " inner task ended");
return "ok";
}).ConfigureAwait(false); //<--------
}
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