.NET Framework 4.0前实现多线程需要使用Thread实现,从.NET Framework 4.0开始可以使用Task实现多线程,使用Task实现多线程,性能更好代码也更简单。本文主要介绍一下.NET Core中,单个多个Task的使用的方法及示例代码。

1、创建实例化单个Task

以下示例创建并执行四个任务。三个任务执行名为的Action <T>委托action,该委托接受Object类型的参数。第四个任务执行一个lambda表达式(一个Action委托),该表达式在任务创建方法的调用中内联定义。每个任务都被实例化并以不同的方式运行:

1) t1是通过调用任务类构造函数实例化,但通过调用其Start()任务后,才方法t2已经开始。

2) t2通过调用TaskFactory.StartNew(Action<Object>, Object)方法在单个方法调用中实例化和启动Task。

3) t3通过调用Run(Action) 方法在单个方法调用中实例化和启动任务。

4) t4通过调用RunSynchronously() 方法在主线程上同步执行任务。

Task是t4同步执行的,则它在主应用程序线程上执行。其余Task通常在一个或多个线程池线程上异步执行。

using System;
using System.Threading;
using System.Threading.Tasks;
class Example
{
    static void Main()
    {
        Action<object> action = (object obj) =>
                                {
                                   Console.WriteLine("Task={0}, obj={1}, Thread={2}",
                                   Task.CurrentId, obj,
                                   Thread.CurrentThread.ManagedThreadId);
                                };
        //创建一个任务,但不要启动它。
        Task t1 = new Task(action, "alpha");
        // 构造一个已启动的任务
        Task t2 = Task.Factory.StartNew(action, "beta");
        // Block the main thread to demonstrate that t2 is executing
        t2.Wait();
        // 执行 t1 
        t1.Start();
        Console.WriteLine("t1 has been launched. (Main Thread={0})",
                          Thread.CurrentThread.ManagedThreadId);
        // 等待task执行完成.
        t1.Wait();
        // 使用task . run构造一个已启动的任务。
        String taskData = "delta";
        Task t3 = Task.Run( () => {Console.WriteLine("Task={0}, obj={1}, Thread={2}",
                                                     Task.CurrentId, taskData,
                                                      Thread.CurrentThread.ManagedThreadId);
                                   });
        // 等待task执行完成.
        t3.Wait();
        // 构造一个未启动的任务
        Task t4 = new Task(action, "gamma");
        // 同步运行它
        t4.RunSynchronously();
       //虽然任务是同步运行的,但这是一个很好的实践
        //在任务抛出的事件异常中等待它。
        t4.Wait();
    }
}
// The example displays output like the following:
//       Task=1, obj=beta, Thread=3
//       t1 has been launched. (Main Thread=1)
//       Task=2, obj=alpha, Thread=4
//       Task=3, obj=delta, Thread=3
//       Task=4, obj=gamma, Thread=1

2、创建和执行任务(Task)

可以以多种方式创建任务(task)实例。从.NET Framework 4.5开始可用的最常见方法是调用静态Run方法。该运行方法提供了一种简单的方法使用默认值来启动一个任务,而不需要额外的参数。下面的示例使用Run(Action)方法启动一个循环任务,然后显示循环迭代次数:

using System;
using System.Threading.Tasks;
public class Example
{
   public static async Task Main()
   {
      await Task.Run( () => {
                                  // Just loop.
                                  int ctr = 0;
                                  for (ctr = 0; ctr <= 1000000; ctr++)
                                  {}
                                  Console.WriteLine("Finished {0} loop iterations",
                                                    ctr);
                               } );
   }
}
// The example displays the following output:
//        Finished 1000001 loop iterations

在.NET Framework 4中启动任务的另一种方法也是最常见的方法是静态TaskFactory.StartNew方法。该Task.Factory属性返回TaskFactory对象。通过TaskFactory.StartNew方法的重载,可以指定要传递给任务创建选项和任务计划程序的参数。下面的示例使用TaskFactory.StartNew方法启动任务。它在功能上等效于先前示例中的代码。

using System;
using System.Threading.Tasks;
public class Example
{
   public static void Main()
   {
      Task t = Task.Factory.StartNew( () => {
                                  // Just loop.
                                  int ctr = 0;
                                  for (ctr = 0; ctr <= 1000000; ctr++)
                                  {}
                                  Console.WriteLine("Finished {0} loop iterations",
                                                    ctr);
                               } );
      t.Wait();
   }
}
// The example displays the following output:
//        Finished 1000001 loop iterations

3、创建执行多个(task)任务(等待一个或多个完成)

任务通常在线程池线程上异步运行,则创建和启动任务的线程会在实例化任务后立即继续执行。在某些情况下,当调用线程是主应用程序线程时,该应用程序可能在任何任务实际开始执行之前终止。在其他情况下,您的应用程序逻辑可能要求调用线程仅在一个或多个任务完成执行后才继续执行。您可以通过调用一种Wait方法等待一个或多个任务完成来同步调用线程的执行及其启动的异步任务。

要等待单个任务完成,可以调用其Task.Wait方法。对 Wait方法的调用将阻塞调用线程,直到单个类实例完成执行为止。

下面的示例调用无参数的Wait() 方法无条件地等待直到任务完成。该任务通过调用Thread.Sleep方法Sleep两秒钟来模拟工作:

using System;   
using System.Threading;
using System.Threading.Tasks;
class Program
{
    static Random rand = new Random();
    static void Main()
    {
        // Wait on a single task with no timeout specified.
        Task taskA = Task.Run( () => Thread.Sleep(2000));
        Console.WriteLine("taskA Status: {0}", taskA.Status);
        try {
          taskA.Wait();
          Console.WriteLine("taskA Status: {0}", taskA.Status);
       } 
       catch (AggregateException) {
          Console.WriteLine("Exception in taskA.");
       }   
    }    
}
// The example displays output like the following:
//     taskA Status: WaitingToRun
//     taskA Status: RanToCompletion

也可以有条件地等待任务完成。Wait(Int32) 和Wait(TimeSpan)的方法阻塞调用线程,直到任务完成或超时时间间隔过去时,以先到者为准。由于以下示例启动了一个睡眠状态为两秒但定义了一秒超时值的任务,因此调用线程将阻塞直到超时到期且该任务已完成执行。

using System;
using System.Threading;
using System.Threading.Tasks;
public class Example
{
   public static void Main()
   {
      // Wait on a single task with a timeout specified.
      Task taskA = Task.Run( () => Thread.Sleep(2000));
      try {
        taskA.Wait(1000);       // Wait for 1 second.
        bool completed = taskA.IsCompleted;
        Console.WriteLine("Task A completed: {0}, Status: {1}",
                         completed, taskA.Status);
        if (! completed)
           Console.WriteLine("Timed out before task A completed.");                 
       }
       catch (AggregateException) {
          Console.WriteLine("Exception in taskA.");
       }   
   }
}
// The example displays output like the following:
//     Task A completed: False, Status: Running
//     Timed out before task A completed.

还可以通过调用Wait(CancellationToken) 和 Wait(Int32, CancellationToken) 方法来提供取消令牌。如果在执行Wait方法时令牌的IsCancellationRequested属性为true或变为true,则该方法将抛出OperationCanceledException

在某些情况下,您可能要等待一系列执行任务中的第一个完成,但是不在乎它是哪个任务。为此,可以调用Task.WaitAny方法的重载之一。下面的示例创建三个任务,每个任务休眠一个时间间隔(由随机数生成器确定)。该了WaitAny(任务[])用于第一任务方法等待完成。然后,该示例显示有关所有三个任务状态的信息。

using System;
using System.Threading;
using System.Threading.Tasks;
public class Example
{
   public static void Main()
   {
      var tasks = new Task[3];
      var rnd = new Random();
      for (int ctr = 0; ctr <= 2; ctr++)
         tasks[ctr] = Task.Run( () => Thread.Sleep(rnd.Next(500, 3000)));
      try {
         int index = Task.WaitAny(tasks);
         Console.WriteLine("Task #{0} completed first.\n", tasks[index].Id);
         Console.WriteLine("Status of all tasks:");
         foreach (var t in tasks)
            Console.WriteLine("   Task #{0}: {1}", t.Id, t.Status);
      }
      catch (AggregateException) {
         Console.WriteLine("An exception occurred.");
      }
   }
}
// The example displays output like the following:
//     Task #1 completed first.
//     
//     Status of all tasks:
//        Task #3: Running
//        Task #1: RanToCompletion
//        Task #4: Running

还可以通过调用WaitAll方法来等待所有一系列任务完成。下面的示例创建十个任务,等待所有十个任务完成,然后显示其状态:

using System;
using System.Threading;
using System.Threading.Tasks;
public class Example
{
   public static void Main()
   {
      // Wait for all tasks to complete.
      Task[] tasks = new Task[10];
      for (int i = 0; i < 10; i++)
      {
          tasks[i] = Task.Run(() => Thread.Sleep(2000));
      }
      try {
         Task.WaitAll(tasks);
      }
      catch (AggregateException ae) {
         Console.WriteLine("One or more exceptions occurred: ");
         foreach (var ex in ae.Flatten().InnerExceptions)
            Console.WriteLine("   {0}", ex.Message);
      }   
      Console.WriteLine("Status of completed tasks:");
      foreach (var t in tasks)
         Console.WriteLine("   Task #{0}: {1}", t.Id, t.Status);
   }
}
// The example displays the following output:
//     Status of completed tasks:
//        Task #2: RanToCompletion
//        Task #1: RanToCompletion
//        Task #3: RanToCompletion
//        Task #4: RanToCompletion
//        Task #6: RanToCompletion
//        Task #5: RanToCompletion
//        Task #7: RanToCompletion
//        Task #8: RanToCompletion
//        Task #9: RanToCompletion
//        Task #10: RanToCompletion

注意:当您等待一个或多个任务完成时,正在运行的任务中引发的所有异常都会在调用该Wait方法的线程上传播,如以下示例所示。它启动12个任务,其中三个正常完成,另外三个抛出异常。在其余六个任务中,三个任务在开始之前被取消,而三个在执行时被取消。异常在WaitAll方法调用中引发,并由try/ catch块处理:

using System;
using System.Threading;
using System.Threading.Tasks;
public class Example
{
   public static void Main()
   {
      // 创建一个取消令牌并取消它。
      var source1 = new CancellationTokenSource();
      var token1 = source1.Token;
      source1.Cancel();
      // 为以后的取消创建一个取消令牌
      var source2 = new CancellationTokenSource();
      var token2 = source2.Token;
      // 创建一系列将会完成、被取消的任务,
      // 超时,或抛出异常。
      Task[] tasks = new Task[12];
      for (int i = 0; i < 12; i++)
      {
          switch (i % 4) 
          {
             // Task should run to completion.
             case 0:
                tasks[i] = Task.Run(() => Thread.Sleep(2000));
                break;
             // Task should be set to canceled state.
             case 1:   
                tasks[i] = Task.Run( () => Thread.Sleep(2000),
                         token1);
                break;         
             case 2:
                // Task should throw an exception.
                tasks[i] = Task.Run( () => { throw new NotSupportedException(); } );
                break;
             case 3:
                // 任务Task应该检查取消令牌。
                tasks[i] = Task.Run( () => { Thread.Sleep(2000); 
                                             if (token2.IsCancellationRequested)
                                                token2.ThrowIfCancellationRequested();
                                             Thread.Sleep(500); }, token2);   
                break;
          }
      }
      Thread.Sleep(250);
      source2.Cancel();
      try {
         Task.WaitAll(tasks);
      }
      catch (AggregateException ae) {
          Console.WriteLine("One or more exceptions occurred:");
          foreach (var ex in ae.InnerExceptions)
             Console.WriteLine("   {0}: {1}", ex.GetType().Name, ex.Message);
       }   
      Console.WriteLine("\nStatus of tasks:");
      foreach (var t in tasks) {
         Console.WriteLine("   Task #{0}: {1}", t.Id, t.Status);
         if (t.Exception != null) {
            foreach (var ex in t.Exception.InnerExceptions)
               Console.WriteLine("      {0}: {1}", ex.GetType().Name,
                                 ex.Message);
         }
      }
   }
}
// The example displays output like the following:
//   One or more exceptions occurred:
//      TaskCanceledException: A task was canceled.
//      NotSupportedException: Specified method is not supported.
//      TaskCanceledException: A task was canceled.
//      TaskCanceledException: A task was canceled.
//      NotSupportedException: Specified method is not supported.
//      TaskCanceledException: A task was canceled.
//      TaskCanceledException: A task was canceled.
//      NotSupportedException: Specified method is not supported.
//      TaskCanceledException: A task was canceled.
//   
//   Status of tasks:
//      Task #13: RanToCompletion
//      Task #1: Canceled
//      Task #3: Faulted
//         NotSupportedException: Specified method is not supported.
//      Task #8: Canceled
//      Task #14: RanToCompletion
//      Task #4: Canceled
//      Task #6: Faulted
//         NotSupportedException: Specified method is not supported.
//      Task #7: Canceled
//      Task #15: RanToCompletion
//      Task #9: Canceled
//      Task #11: Faulted
//         NotSupportedException: Specified method is not supported.
//      Task #12: Canceled

参考文档system.threading.tasks.task

相关文档.NET(C#) task RunSynchronously()和Start()的使用与区别

推荐文档