Under Mono and MonoTouch, I am seeing an approx 500 millisecond delay between when I call:
StartNew(Action<object> action, object state, CancellationToken cancellationToken,
TaskCreationOptions creationOptions, TaskScheduler scheduler);
and when the worker code actually starts executing.
I created a test to show this:
public static class TestTaskFactory
{
private class TaskInfo
{
public int Number;
}
private static int NUM_TASKS = 5;
private static int NumFinished = 0;
public static void Run()
{
for (int n = 1; n <= NUM_TASKS; n++)
{
Log("Starting task #" + n + " ...");
var task_info = new TaskInfo { Number = n };
var task = Task.Factory.StartNew(Worker, task_info, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
Thread.Sleep(0);
}
Log("Waiting for tasks to finish ...");
while (NumFinished < NUM_TASKS)
{
Thread.Sleep(1);
}
Log("All done");
}
private static void Worker(object state)
{
var task_info = (TaskInfo)state;
Log("Task #" + task_info.Number + " running");
// Do something
Thread.Sleep(2000);
// Done
++NumFinished;
}
private static void Log(string msg)
{
Console.WriteLine(DateTime.Now.ToString("HH.mm.ss.fff") + ": Thread " + Thread.CurrentThread.ManagedThreadId + ": " + msg);
}
}
Output under Mono on Mac:
16.57.31.420: Thread 1: Starting task #1 ...
16.57.31.508: Thread 1: Starting task #2 ...
16.57.31.508: Thread 1: Starting task #3 ...
16.57.31.508: Thread 1: Starting task #4 ...
16.57.31.508: Thread 1: Starting task #5 ...
16.57.31.508: Thread 1: Waiting for tasks to finish ...
16.57.31.510: Thread 5: Task #1 running
16.57.32.009: Thread 6: Task #2 running <-- Approx 500 msec later
16.57.32.511: Thread 7: Task #3 running <-- Approx 500 msec later
16.57.33.012: Thread 8: Task #4 running <-- Approx 500 msec later
16.57.33.513: Thread 9: Task #5 running <-- Approx 500 msec later
16.57.35.515: Thread 1: All done
It is as if Mono wants to wait up to 500 msec to reuse an existing thread before spawing a new one. If I decrease the worker time below 500 msec, the delay decreases. For example, changing the worker Thread.Sleep(2000) to Thread.Sleep(50):
...
17.13.20.262: Thread 5: Task #1 running
17.13.20.314: Thread 5: Task #2 running <-- approx 50 msec later
17.13.20.365: Thread 5: Task #3 running <-- approx 50 msec later
17.13.20.416: Thread 5: Task #4 running <-- approx 50 msec later
17.13.20.466: Thread 5: Task #5 running <-- approx 50 msec later
But under MS Framework 4.0, no large delay before worker code starts:
...
17.05.42.238: Thread 9: Waiting for tasks to finish ...
17.05.42.256: Thread 11: Task #1 running
17.05.42.256: Thread 12: Task #3 running <-- little delay
17.05.42.256: Thread 13: Task #4 running <-- little delay
17.05.42.257: Thread 10: Task #2 running <-- little delay
17.05.43.264: Thread 14: Task #5 running <-- little delay
Before I submit a bug report on Mono, I wanted to sanity check that I am not missing some tweak I need to make on Mono or using Task.Factory wrong. I am actually using a max concurrency scheduler in my real app.
So my question: is this a bug in Mono/MonoTouch?
Update: I've switched from using ThreadPool under Mono* to Ami Bar's Smart Thread Pool (github; Code Project article). GSerjo's Extended Thread Pool also looked nice, but had alot of dependencies that I was trying to avoid on mobile. I wrote up some of my simple testing at a Xamarim thread. I probably missed 100 other thread pool implementations, but I've been happy with SmartThreadPool so far. Use WINDOWS_PHONE mode to compile under MonoTouch.
Not necessarily. I suspect it's just the thread pool being unwilling to start more than one new thread every 500ms. Note that you're seeing the first task start up pretty much instantly. It's only after that that you're seeing a delay.
If you use more tasks on .NET 4.5, you'll see something similar, except with "chunks" of threads starting each second.
You may find that calling
ThreadPool.SetMinThreads
helps, assuming that's available in MonoTouch.