在异步执行任务数组时,不应该只运行最长的任务吗?

时间:2017-05-26 00:25:45

标签: c# multithreading asynchronous task

我有10个任务列表,每个任务需要15秒。所有任务都在一个数组中,并以异步方式执行。整套不应该花费大约15秒钟吗?从下面的代码中,请注意在输出中整个集合需要21秒。当我将其更改为100个任务时,需要一分钟。它几乎就像创建任务需要一秒钟一样。我错过了什么?谢谢!

static void Main(string[] args)
{
    var mainStart = DateTime.Now;
    var tasks = new List<Task>();
    for (int i = 0; i < 10; i++)
    {
        tasks.Add(Task.Factory.StartNew((Object data) =>
        {
            var index = (int)data;
            var stepStart = DateTime.Now;
            Console.WriteLine("{0} Starting {1} on thread {2}...", stepStart, index, Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(15000);
            var stepFinish = DateTime.Now;
            Console.WriteLine("{0} Finished {1} on thread {2}, duration: {3}",
                stepStart, index, Thread.CurrentThread.ManagedThreadId, stepFinish - stepStart);
        },
        i));
    }
    Task.WaitAll(tasks.ToArray());
    var mainFinish = DateTime.Now;
    Console.WriteLine("{0} Finished, duration {1}", DateTime.Now, mainFinish - mainStart);
    Console.WriteLine("Press any key to exit.");
    Console.Read();

    // Output
    //5/25/2017 8:03:43 PM Starting 0 on thread 10...
    //5/25/2017 8:03:43 PM Starting 1 on thread 11...
    //5/25/2017 8:03:43 PM Starting 2 on thread 12...
    //5/25/2017 8:03:43 PM Starting 3 on thread 13...
    //5/25/2017 8:03:44 PM Starting 4 on thread 14...
    //5/25/2017 8:03:45 PM Starting 5 on thread 15...
    //5/25/2017 8:03:46 PM Starting 6 on thread 16...
    //5/25/2017 8:03:47 PM Starting 7 on thread 17...
    //5/25/2017 8:03:48 PM Starting 8 on thread 18...
    //5/25/2017 8:03:49 PM Starting 9 on thread 19...
    //5/25/2017 8:03:43 PM Finished 0 on thread 10, duration: 00:00:15.0018957
    //5/25/2017 8:03:43 PM Finished 1 on thread 11, duration: 00:00:15.0175209
    //5/25/2017 8:03:43 PM Finished 2 on thread 12, duration: 00:00:15.0175209
    //5/25/2017 8:03:43 PM Finished 3 on thread 13, duration: 00:00:15.0165291
    //5/25/2017 8:03:44 PM Finished 4 on thread 14, duration: 00:00:15.0156567
    //5/25/2017 8:03:45 PM Finished 5 on thread 15, duration: 00:00:15.0156012
    //5/25/2017 8:03:46 PM Finished 6 on thread 16, duration: 00:00:15.0155997
    //5/25/2017 8:03:47 PM Finished 7 on thread 17, duration: 00:00:15.0155989
    //5/25/2017 8:03:48 PM Finished 8 on thread 18, duration: 00:00:15.0155985
    //5/25/2017 8:03:49 PM Finished 9 on thread 19, duration: 00:00:15.0156328
    //5/25/2017 8:04:04 PM Finished, duration 00:00:21.0322775
    //Press any key to exit.
}

4 个答案:

答案 0 :(得分:1)

任务不是线程。如果您想保证所有这些任务同时并行运行,请尝试以下方法:

class Program
{
    static void Main(string[] args)
    {
        var mainStart = DateTime.Now;
        var threads = new List<Thread>();
        for (int i = 0; i < 10; i++)
        {
            threads.Add(new Thread(() =>
            {
                var stepStart = DateTime.Now;
                Console.WriteLine("{0} Starting {1} on thread {2}...", stepStart, i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(15000);
                var stepFinish = DateTime.Now;
                Console.WriteLine("{0} Finished {1} on thread {2}, duration: {3}",
                    stepStart, i, Thread.CurrentThread.ManagedThreadId, stepFinish - stepStart);
            }));
        }

        foreach (Thread t in threads)
        {
            t.Start(); // Starts all the threads
        }

        foreach(Thread t in threads)
        {
            t.Join(); // Make the main thread wait for the others
        }

        var mainFinish = DateTime.Now;
        Console.WriteLine("{0} Finished, duration {1}", DateTime.Now, mainFinish - mainStart);
        Console.WriteLine("Press any key to exit.");
        Console.Read();
    }
}

阅读更多关于主要差异的here

正如Jon Skeet所说:

  

线程是一个较低级别的概念:如果你直接启动一个线程,你知道它将是一个单独的线程,而不是在线程池等上执行。

     

任务不仅仅是一个抽象的&#34;在哪里运行一些代码&#34;虽然 - 它真的只是&#34;未来结果的承诺&#34;。

答案 1 :(得分:1)

名义上,异步工作允许其他事情在他们等待某事时继续......而他们这样做的方式是等待时间的东西(这通常是某种我的某种东西) / O)。为了看到这一点,你可以真正地运行工作异步时尚。例如:

static void Main( string[ ] args )
{
  var totalTime = DoSomeAsyncTasks( ).GetAwaiter( ).GetResult( );
  Console.WriteLine( "{0} Finished, duration {1}", DateTime.Now, totalTime );
  Console.WriteLine( "Press any key to exit." );
  Console.Read( );
}

async static Task<TimeSpan> DoSomeAsyncTasks( )
{
  var mainStart = DateTime.Now;
  var tasks = new List<Task>( );
  for ( int i = 0; i < 10; i++ )
  {
    var id = i;
    tasks.Add( DoATask( id ) );
  }
  await Task.WhenAll( tasks );
  var mainFinish = DateTime.Now;
  return mainFinish - mainStart;
}

static async Task DoATask( int stepId )
{
  var stepStart = DateTime.Now;

  Console.WriteLine(
    "{0} Starting {1} on thread {2}...",
    stepStart, stepId, Thread.CurrentThread.ManagedThreadId );

  //--> more accurately model waiting for I/O...
  await Task.Delay( TimeSpan.FromSeconds( 15 ) );

  var stepFinish = DateTime.Now;
  Console.WriteLine( "{0} Finished {1} on thread {2}, duration: {3}",
      stepStart, stepId, Thread.CurrentThread.ManagedThreadId, stepFinish - stepStart );
}

...返回:

5/25/2017 11:24:36 PM Starting 0 on thread 9...
5/25/2017 11:24:36 PM Starting 1 on thread 9...
5/25/2017 11:24:36 PM Starting 2 on thread 9...
5/25/2017 11:24:36 PM Starting 3 on thread 9...
5/25/2017 11:24:36 PM Starting 4 on thread 9...
5/25/2017 11:24:36 PM Starting 5 on thread 9...
5/25/2017 11:24:36 PM Starting 6 on thread 9...
5/25/2017 11:24:36 PM Starting 7 on thread 9...
5/25/2017 11:24:36 PM Starting 8 on thread 9...
5/25/2017 11:24:36 PM Starting 9 on thread 9...
5/25/2017 11:24:36 PM Finished 9 on thread 11, duration: 00:00:15.0085175
5/25/2017 11:24:36 PM Finished 8 on thread 12, duration: 00:00:15.0085175
5/25/2017 11:24:36 PM Finished 7 on thread 13, duration: 00:00:15.0315198
5/25/2017 11:24:36 PM Finished 6 on thread 14, duration: 00:00:15.0325121
5/25/2017 11:24:36 PM Finished 5 on thread 12, duration: 00:00:15.0335121
5/25/2017 11:24:36 PM Finished 3 on thread 11, duration: 00:00:15.0335121
5/25/2017 11:24:36 PM Finished 2 on thread 12, duration: 00:00:15.0355229
5/25/2017 11:24:36 PM Finished 1 on thread 11, duration: 00:00:15.0355229
5/25/2017 11:24:36 PM Finished 4 on thread 14, duration: 00:00:15.0335121
5/25/2017 11:24:36 PM Finished 0 on thread 13, duration: 00:00:15.0545213
5/25/2017 11:24:51 PM Finished, duration 00:00:15.0665191

(我做了所有相同的代码 - 并且接受的答案确实......只是将它扩展到方法中以使其更具说明性。)

您应该从中看到的是,每项任务大约在同一时间开始,每项任务大约需要15秒,但总运行时间也大约为15秒。这是因为,当任务正在等待时,它会让其他工作运行。还要注意,一切都在同一个单线程上运行。这项工作是等待的。它非常酷 - 可能更像你可能期待的那样。

答案 2 :(得分:1)

  

我错过了什么?

线程池的工作原理。

您可以将线程池​​视为线程集合,以及要完成的工作队列。通常,线程池的线程集合与CPU核心数大致相同 - 因为一次只有那么多线程可以实际执行(即运行代码)。

此外,当线程池比线程有更多工作要做时,它会向其线程集合添加更多线程。但它限制了线程注入速率 - IIRC,当前线程注入速率就像每2秒一个新线程。这种限制对于防止螺纹颠簸是必要的;创建和销毁线程的成本很高,因此线程池使用注入速率限制作为启发式。

所以,one answer这里通过使用普通线程来避免线程池(在现实世界的代码中我永远不推荐)。 Another answer通过使用异步任务来避免线程池。这个答案只是解释了为什么你看到了这种行为。

但是如果你想同时(和同步) on 线程池运行它们,你可以通过告诉线程池到increase its minimum number of threads来做到这一点。

答案 3 :(得分:0)

Factory.StartNew只是在TaskScheduler上排队任务 - 如果所有线程都忙于其他工作,它就不会立即运行任务。如果您记录开始时间,那么您将看到其中至少有一项任务没有启动6秒。