多个任务变慢

ebb*_*ebb 17 c# multithreading

代码:

static void DoIt(string name)
{
    Console.WriteLine("Hello {0} | {1}", name, Thread.CurrentThread.ManagedThreadID);
    Thread.Sleep(5000);
    Console.WriteLine("Bye {0} | {1}", name, Thread.CurrentThread.ManagedThreadID);
}

static void Main()
{
    Task.Factory.StartNew(() => DoIt("One"));
    Task.Factory.StartNew(() => DoIt("Two"));
    Task.Factory.StartNew(() => DoIt("Three"));
    Task.Factory.StartNew(() => DoIt("Four"));
    Task.Factory.StartNew(() => DoIt("Five"));
    Task.Factory.StartNew(() => DoIt("Six"));
    Task.Factory.StartNew(() => DoIt("Seven"));
    Task.Factory.StartNew(() => DoIt("Eight"));
    Task.Factory.StartNew(() => DoIt("Nine"));
    Task.Factory.StartNew(() => DoIt("Ten"));

    Console.ReadKey();
}
Run Code Online (Sandbox Code Playgroud)

为什么它可以立即精确启动前3个任务,但是任务4启动需要5-10秒,在任务4启动后,任务5启动前需要5-10秒,依此类推.GC是做什么的吗?有人可以澄清一下发生了什么吗?

Ree*_*sey 18

为什么它可以立即精确启动前3个任务,但是任务4启动需要5-10秒,在任务4启动后,任务5启动前需要5-10秒,依此类推.GC是做什么的吗?有人可以澄清一下发生了什么吗?

默认情况下,第一次运行此命令时,ThreadPool将使用最小工作线程数分配.在安排了前4个任务之后,线程池将"加速"以便随着时间的推移处理更多,这就是您看到延迟的原因.

在我的系统(有8个核心)上,前8个是即时的,然后接下来的两个在一秒后启动.

在您的情况下,如果您第二次运行测试,则线程将立即启动.这是因为,在第一次运行之后,ThreadPool应该有足够的工作人员立即安排这个.

请尝试以下操作以查看此行为.如果您将SetMinThreads呼叫保留在原地,则会立即安排这些呼叫.如果你注释掉它,你会看到,第一次,它需要一段时间,但第二次通过(假设你等待线程完成),线程将立即运行.

static void DoIt(string name)
{
    Console.WriteLine("Hello {0} | {1} - {2}", name, Thread.CurrentThread.ManagedThreadId, DateTime.Now);
    Thread.Sleep(5000);
    Console.WriteLine("Bye {0} | {1} - {2}", name, Thread.CurrentThread.ManagedThreadId, DateTime.Now);
}

static void Main()
{
    int workerThreads, complete;
    ThreadPool.GetMinThreads(out workerThreads, out complete);

    Console.WriteLine(workerThreads);

    // Comment out this line to see the difference...
    // WIth this commented out, the second iteration will be immediate
    ThreadPool.SetMinThreads(100, complete);

    Action run = () =>
        {
            for (int i = 0; i < 20; ++i)
            {
                int tmp = i;
                Task.Factory.StartNew(() => DoIt(tmp.ToString()));
            }
        };

    run();
    Console.WriteLine("Press a key to run again...");
    Console.ReadKey();

    run();

    Console.WriteLine("Press a key to exit...");
    Console.ReadKey();
}
Run Code Online (Sandbox Code Playgroud)

请注意,这种行为实际上与整个TPL没什么关系 - 它更多是默认TaskScheduler使用的,只是将任务传递给了ThreadPool.例如,如果你LongRunningStartNew()调用中使用提示设置这些线程,它们都会立即启动(因为默认调度程序将设置一个新的专用线程并立即执行它).


Ada*_*ras 6

任务没有减慢,它们被任务并行库排队.CLR知道您的计算机上有多少逻辑核心可用; TPL线程池使用此信息来确定要提供的工作线程数.在您的情况下,您可能有四个逻辑核心; 为主线程(Main()正在运行)取一个三个核心(和三个线程池工作者)仍然执行任务.