Use Task.Run instead of Delegate.BeginInvoke

后端 未结 3 435
被撕碎了的回忆
被撕碎了的回忆 2021-02-06 08:11

I have recently upgraded my projects to ASP.NET 4.5 and I have been waiting a long time to use 4.5\'s asynchronous capabilities. After reading the documentation I\'m not sure wh

3条回答
  •  忘了有多久
    2021-02-06 08:34

    It may sound a bit out of scope, but if you just want to forget after you launch it, why not using directly ThreadPool?

    Something like:

    ThreadPool.QueueUserWorkItem(
                x =>
                    {
                        try
                        {
                            // Do something
                            ...
                        }
                        catch (Exception e)
                        {
                            // Log something
                            ...
                        }
                    });
    

    I had to do some performance benchmarking for different async call methods and I found that (not surprisingly) ThreadPool works much better, but also that, actually, BeginInvoke is not that bad (I am on .NET 4.5). That's what I found out with the code at the end of the post. I did not find something like this online, so I took the time to check it myself. Each call is not exactly equal, but it is more or less functionally equivalent in terms of what it does:

    1. ThreadPool: 70.80ms
    2. Task: 90.88ms
    3. BeginInvoke: 121.88ms
    4. Thread: 4657.52ms

      public class Program
      {
          public delegate void ThisDoesSomething();
      
          // Perform a very simple operation to see the overhead of
          // different async calls types.
          public static void Main(string[] args)
          {
              const int repetitions = 25;
              const int calls = 1000;
              var results = new List>();
      
              Console.WriteLine(
                  "{0} parallel calls, {1} repetitions for better statistics\n", 
                  calls, 
                  repetitions);
      
              // Threads
              Console.Write("Running Threads");
              results.Add(new Tuple("Threads", RunOnThreads(repetitions, calls)));
              Console.WriteLine();
      
              // BeginInvoke
              Console.Write("Running BeginInvoke");
              results.Add(new Tuple("BeginInvoke", RunOnBeginInvoke(repetitions, calls)));
              Console.WriteLine();
      
              // Tasks
              Console.Write("Running Tasks");
              results.Add(new Tuple("Tasks", RunOnTasks(repetitions, calls)));
              Console.WriteLine();
      
              // Thread Pool
              Console.Write("Running Thread pool");
              results.Add(new Tuple("ThreadPool", RunOnThreadPool(repetitions, calls)));
              Console.WriteLine();
              Console.WriteLine();
      
              // Show results
              results = results.OrderBy(rs => rs.Item2).ToList();
              foreach (var result in results)
              {
                  Console.WriteLine(
                      "{0}: Done in {1}ms avg", 
                      result.Item1,
                      (result.Item2 / repetitions).ToString("0.00"));
              }
      
              Console.WriteLine("Press a key to exit");
              Console.ReadKey();
          }
      
          /// 
          /// The do stuff.
          /// 
          public static void DoStuff()
          {
              Console.Write("*");
          }
      
          public static double RunOnThreads(int repetitions, int calls)
          {
              var totalMs = 0.0;
              for (var j = 0; j < repetitions; j++)
              {
                  Console.Write(".");
                  var toProcess = calls;
                  var stopwatch = new Stopwatch();
                  var resetEvent = new ManualResetEvent(false);
                  var threadList = new List();
                  for (var i = 0; i < calls; i++)
                  {
                      threadList.Add(new Thread(() =>
                      {
                          // Do something
                          DoStuff();
      
                          // Safely decrement the counter
                          if (Interlocked.Decrement(ref toProcess) == 0)
                          {
                              resetEvent.Set();
                          }
                      }));
                  }
      
                  stopwatch.Start();
                  foreach (var thread in threadList)
                  {
                      thread.Start();
                  }
      
                  resetEvent.WaitOne();
                  stopwatch.Stop();
                  totalMs += stopwatch.ElapsedMilliseconds;
              }
      
              return totalMs;
          }
      
          public static double RunOnThreadPool(int repetitions, int calls)
          {
              var totalMs = 0.0;
              for (var j = 0; j < repetitions; j++)
              {
                  Console.Write(".");
                  var toProcess = calls;
                  var resetEvent = new ManualResetEvent(false);
                  var stopwatch = new Stopwatch();
                  var list = new List();
                  for (var i = 0; i < calls; i++)
                  {
                      list.Add(i);
                  }
      
                  stopwatch.Start();
                  for (var i = 0; i < calls; i++)
                  {
                      ThreadPool.QueueUserWorkItem(
                          x =>
                          {
                              // Do something
                              DoStuff();
      
                              // Safely decrement the counter
                              if (Interlocked.Decrement(ref toProcess) == 0)
                              {
                                  resetEvent.Set();
                              }
                          },
                          list[i]);
                  }
      
                  resetEvent.WaitOne();
                  stopwatch.Stop();
                  totalMs += stopwatch.ElapsedMilliseconds;
              }
      
              return totalMs;
          }
      
          public static double RunOnBeginInvoke(int repetitions, int calls)
          {
              var totalMs = 0.0;
              for (var j = 0; j < repetitions; j++)
              {
                  Console.Write(".");
                  var beginInvokeStopwatch = new Stopwatch();
                  var delegateList = new List();
                  var resultsList = new List();
                  for (var i = 0; i < calls; i++)
                  {
                      delegateList.Add(DoStuff);
                  }
      
                  beginInvokeStopwatch.Start();
                  foreach (var delegateToCall in delegateList)
                  {
                      resultsList.Add(delegateToCall.BeginInvoke(null, null));
                  }
      
                  // We lose a bit of accuracy, but if the loop is big enough,
                  // it should not really matter
                  while (resultsList.Any(rs => !rs.IsCompleted))
                  {
                      Thread.Sleep(10);
                  }
      
                  beginInvokeStopwatch.Stop();
                  totalMs += beginInvokeStopwatch.ElapsedMilliseconds;
              }
      
              return totalMs;
          }
      
          public static double RunOnTasks(int repetitions, int calls)
          {
              var totalMs = 0.0;
              for (var j = 0; j < repetitions; j++)
              {
                  Console.Write(".");
                  var resultsList = new List();
                  var stopwatch = new Stopwatch();
                  stopwatch.Start();
                  for (var i = 0; i < calls; i++)
                  {
                      resultsList.Add(Task.Factory.StartNew(DoStuff));
                  }
      
                  // We lose a bit of accuracy, but if the loop is big enough,
                  // it should not really matter
                  while (resultsList.Any(task => !task.IsCompleted))
                  {
                      Thread.Sleep(10);
                  }
      
                  stopwatch.Stop();
                  totalMs += stopwatch.ElapsedMilliseconds;
              }
      
              return totalMs;
          }
      }
      

提交回复
热议问题