Task.Factory.StartNew() 在Mono / MonoTouch下的延迟问题

6
在Mono和MonoTouch下,我发现当我调用以下代码时会出现大约500毫秒的延迟:
StartNew(Action<object> action, object state, CancellationToken cancellationToken, 
    TaskCreationOptions creationOptions, TaskScheduler scheduler);

当工作代码实际开始执行时,会发生以下情况。我创建了一个测试来说明这一点:
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);
    }
}

在Mac上使用Mono输出的结果:
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

似乎 Mono 想要在使用现有线程之前等待最多 500 毫秒。如果我将 worker 时间降低到 500 毫秒以下,延迟就会减少。例如,将 worker 的 Thread.Sleep(2000) 改为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

但在 MS Framework 4.0 下,工作代码启动前没有的延迟:

...
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

在我提交Mono的错误报告之前,我想要先确认一下是否我遗漏了在Mono上做出必要调整或者不正确地使用Task.Factory。实际上,在我的真实应用程序中我正在使用最大并发调度器。
所以我的问题是:这是Mono / MonoTouch的一个bug吗?
更新:我已经从Mono*下使用线程池转向了Ami Bar的Smart Thread Pool(githubCode Project article )。GSerjo的Extended Thread Pool看起来也很好,但是它有太多的依赖项,而我正试图避免在移动端使用太多依赖项。我在Xamarin thread上写了一些简单测试。我可能错过了其他100种线程池实现,但是到目前为止我对SmartThreadPool感到满意。在MonoTouch下编译时,请使用WINDOWS_PHONE模式。
1个回答

6
所以我的问题是:这是Mono/MonoTouch的一个错误吗? 并不一定。我怀疑这只是线程池不愿意每500毫秒启动一个以上的新线程。请注意,你几乎可以立即看到第一个任务启动。只有在那之后才会出现延迟。 如果在.NET 4.5上使用更多任务,你会看到类似的情况,但每秒会开始“块”线程。 你可能会发现调用ThreadPool.SetMinThreads会有帮助,假设在MonoTouch中可用。

4
线程池中线程启动缓慢是为了避免线程风暴。 - miguel.de.icaza
1
你能提供“线程风暴”的定义参考@ miguel.de.icaza吗?我在谷歌上搜不到。 - t9mike
这对于C#5、async/await和Tasks来说是一个更大的问题...使用大量的Tasks会破坏线程池。 - jduncanator
@jduncanator:以什么方式?任务被设计为轻量级,因此可以排队许多任务。还要注意,由async/await创建的任务通常也不是在线程池中启动的任务。 - Jon Skeet
您还可以定义一个环境变量MONO_THREADS_PER_CPU,以设置在强制执行惩罚之前允许的每个CPU线程数。 - jduncanator
显示剩余11条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接