C# Task详解(学习)

原文地址:C# Task详解 - 五维思考 - 博客园 (cnblogs.com)

1. Task 的优势

  ThreadPool 相对于 Thread 具备许多优势,但 ThreadPool 却又存在一些使用上的不方便。

  比如:

    ThreadPool 不支持线程的取消、完成、失败通知等交互性操作。

    ThreadPool 不支持线程执行的先后次序。

  以前,开发者要实现上述功能,需要完成很多额外的工作,现在,FCL中提供了一个功能更强大的概念:Task。Task 在线程池的基础上进行了优化,并提供了更多的API。在FCL4.0中,编写多线程程序,Task显然已经优于传统的方式。

  简单的Demo: 

 1 using System;
 2 using System.Threading;
 3 using System.Threading.Tasks;
 4 
 5 namespace TaskDemo
 6 {
 7     class Program
 8     {
 9         static void Main(string[] args)
10         {
11             Task t = new Task(() =>
12             {
13                 Console.WriteLine($"{DateTime.Now}  任务开始工作...");
14                 // 模拟工作过程
15                 Thread.Sleep(5000);
16             });
17             t.Start();
18             t.ContinueWith((task) =>
19             {
20                 Console.WriteLine($"{DateTime.Now}  任务完成,完成时的状态:");
21                 Console.WriteLine("IsCanceled={0}\tIsCompleted={1}\tIsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted);
22             });
23             Console.ReadKey();
24         }
25     }
26 }

 

2. Task 用法

2.1 创建任务

2.1.1 无返回值的方式

方式1:

1 var t1 = new Task(()=>TaskMethod("Task 1"));
2 t1.Start();
3 Task.WaitAll(t1);    // 等待所有任务结束
4 // 注:任务的状态
5 //    Start 之前为Created
6 //    Start 之后为 WaitingToRun

方式2:

1 Task.Run(()=>TaskMethod("Task 2"));

方式3:

1 Task.Factory.StartNew(()=>TaskMethod("Task 3"));    // 直接异步的方法
2 // 或者
3 var t3 = Task.Factory.StartNew(()=>TaskMethod("Task 3"));
4 Task.WaitAll(t3);    // 等待所有任务结束
5 // 任务的状态:
6 // Start 之前:Running
7 // Start 之后:Running

 

示例:

 1 using System;
 2 using System.Threading;
 3 using System.Threading.Tasks;
 4 
 5 namespace TaskDemo
 6 {
 7     class Program
 8     {
 9        
10         static void Main(string[] args)
11         {
12             
13 
14             #region Demo2
15             var t1 = new Task(()=> TaskMethod("Task 1"));
16             var t2 = new Task(()=> TaskMethod("Task 2"));
17             t2.Start();
18             t1.Start();
19             Task.WaitAll(t1,t2);
20             //Task.Run(()=>TaskMethod("Task 3"));   // 提示不包含该方法定义
21             Task.Factory.StartNew(()=>TaskMethod("Task 4"));
22             // 标记为长时间运行任务,则任务不会使用线程池,而在单独的线程中运行
23             Task.Factory.StartNew(()=>TaskMethod("Task 5"), TaskCreationOptions.LongRunning);
24 
25             #region 常规使用方式
26             Console.WriteLine("主线程执行业务处理。");
27             // 创建任务
28             Task task = new Task(()=>
29             {
30                 Console.WriteLine("使用 System.Threading.Tasks.Task 执行异步操作。");
31                 for (int i = 0; i < 10; i++)
32                 {
33                     Console.WriteLine(i);
34                 }
35             });
36             // 启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler)
37             task.Start();
38             Console.WriteLine("主线程执行其他处理。");
39             task.Wait();
40             #endregion
41 
42             Thread.Sleep(TimeSpan.FromSeconds(1));
43             Console.ReadLine();
44             #endregion
45         }
46 
47         static void TaskMethod(string name)
48         {
49             Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
50                 name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
51         }
52     }
53 }

两次运行不同的执行结果:

 asyn/await 的实现方式:

 1 using System;
 2 using System.Threading.Tasks;
 3 
 4 namespace AsyncAwaitDemo
 5 {
 6     class Program
 7     {
 8         static void Main(string[] args)
 9         {
10             Console.WriteLine("主线程执行业务处理。");
11             AsyncFunction();
12             Console.WriteLine("主线程执行其他处理。");
13             for (int i = 0; i < 10; i++)
14             {
15                 Console.WriteLine(string.Format("Main: i = {0}", i));
16             }
17             Console.ReadLine();
18         }
19 
20         async static void AsyncFunction()
21         {
22             await Task.Delay(1);
23             Console.WriteLine("使用 System.Threading.Tasks.Task 执行异步操作。");
24             for (int i = 0; i < 10; i++)
25             {
26                 Console.WriteLine(string.Format("AsyncFunction: i = {0}", i));
27             }
28         }
29     }
30 }

执行结果:

 

 将 await Task.Delay(1); 移动到 AsyncFunction 方法的最后,执行结果:

 

 (二)带返回值的方式

方式4:

1 Task<int> task = CreateTask("Task 1");
2 task.Start();
3 int result = task.Result;

示例:

 1 using System;
 2 using System.Threading;
 3 using System.Threading.Tasks;
 4 
 5 namespace TaskDemo
 6 {
 7     class Program
 8     {
 9 
10         static void Main(string[] args)
11         { #region Demo3
12             TaskMethod("Main Thread Task");
13 
14             Task<int> task = CreateTask("Task 1");
15             task.Start();
16             int result = task.Result;
17             Console.WriteLine("Task 1 Result is : {0}", result);
18 
19             task = CreateTask("Task 2");
20             // 该任务会运行在主线程中
21             task.RunSynchronously();
22             result = task.Result;
23             Console.WriteLine("Task 2 Result is : {0}", result);
24 
25             task = CreateTask("Task 3");
26             Console.WriteLine(task.Status);
27             task.Start();
28 
29             while (!task.IsCompleted)
30             {
31                 Console.WriteLine(task.Status);
32                 Thread.Sleep(TimeSpan.FromSeconds(0.5));
33             }
34 
35             Console.WriteLine(task.Status);
36             result = task.Result;
37             Console.WriteLine("Task 3 Result is : {0}", result);
38 
39             #region 常规使用方式
40             // 创建任务
41             Task<int> getsumtask = new Task<int>(()=>GetSum());
42             // 启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler)
43             getsumtask.Start();
44             Console.WriteLine("主线程执行其他处理");
45             // 等待任务的完成执行过程。
46             getsumtask.Wait();
47             // 获得任务的执行结果
48             Console.WriteLine("任务执行结果: {0}", getsumtask.Result.ToString());
49             #endregion
50 
51             Console.ReadLine();
52             #endregion
53         }
54 
55         static Task<int> CreateTask(string name)
56         {
57             return new Task<int>(() => TaskMethod(name));
58         }
59 
60         static int TaskMethod(string name)
61         {
62             Console.WriteLine("["+DateTime.Now +"]Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
63                 name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
64             Thread.Sleep(TimeSpan.FromSeconds(2));
65             return 42;
66         }
67 
68         static int GetSum()
69         {
70             int sum = 0;
71             Console.WriteLine("使用 Task 执行异步操作。");
72             for (int i = 0; i < 100; i++)
73             {
74                 sum += i;
75             }
76             return sum;
77         }
78             
79     }
80 }

执行结果:

 async / await 的实现

 1 using System;
 2 using System.Threading.Tasks;
 3 
 4 namespace TaskDemo
 5 {
 6     class Program
 7     {
 8         public static void Main()
 9         {
10             var ret1 = AsyncGetsum();
11             Console.WriteLine($"[{DateTime.Now}]主线程执行其他处理。");
12             for (int i = 1; i <= 3; i++)
13                 Console.WriteLine($"[{DateTime.Now}]Call Main()");
14             int result = ret1.Result;                  //阻塞主线程
15             Console.WriteLine("["+DateTime.Now+"]任务执行结果:{0}", result);
16         }
17 
18         async static Task<int> AsyncGetsum()
19         {
20             await Task.Delay(1);
21             int sum = 0;
22             Console.WriteLine($"[{DateTime.Now}]使用 Task 执行异步操作");
23             for (int i = 0; i < 100; i++)
24             {
25                 sum += i;
26             }
27             return sum;
28         }
29     }
30 }

执行结果:

 

 2.2 组合任务  ContinueWith

简单Demo:

 1 using System;
 2 using System.Threading.Tasks;
 3 
 4 namespace ContiuneWithDemo
 5 {
 6     class Program
 7     {
 8         static void Main(string[] args)
 9         {
10             #region Demo1
11             // 创建一个任务
12             Task<int> task = new Task<int>(() =>
13             {
14                 int sum = 0;
15                 Console.WriteLine("使用 Task 执行异步操作。");
16                 for (int i = 0; i < 100; i++)
17                 {
18                     sum += i;
19                 }
20                 return sum;
21             });
22             // 启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler)
23             task.Start();
24             // 任务完成时执行处理。
25             Task cwt = task.ContinueWith(t =>
26             {
27                 Console.WriteLine("任务完成后的执行结果:{0}", t.Result.ToString());
28             });
29             task.Wait();
30             cwt.Wait();
31             Console.ReadKey();
32             #endregion
33         }
34     }
35 }

执行结果:

 

 任务的串行:

 1 ConcurrentStack<int> stack = new ConcurrentStack<int>();
 2 // t1 先串行
 3 var t1 = Task.Factory.StartNew(() =>
 4 {
 5    stack.Push(1);
 6    stack.Push(2);
 7 });
 8 // t2,t3 并行执行
 9 var t2 = t1.ContinueWith(t =>
10 {
11    int result;
12    stack.TryPop(out result);
13    Console.WriteLine("Task t2 result={0}, Thread id {1}", result, Thread.CurrentThread.ManagedThreadId);
14 });
15 // t2,t3 并行执行
16 var t3 = t1.ContinueWith(t =>
17 {
18    int result;
19    stack.TryPop(out result);
20    Console.WriteLine("Task t3 result={0}, Thread id {1}", result, Thread.CurrentThread.ManagedThreadId);
21 });
22 // 等待 t2 和 t3 执行完
23 Task.WaitAll(t2, t3);
24 // t4 串行执行
25 var t4 = Task.Factory.StartNew(() =>
26 {
27    Console.WriteLine("当前集合元素个数:{0}, Thread id {1}", stack.Count, Thread.CurrentThread.ManagedThreadId);
28 });
29 t4.Wait();
30 
31 Console.ReadKey();

 执行结果:

子任务:

 1 using System;
 2 using System.Threading.Tasks;
 3 
 4 namespace ContiuneWithDemo
 5 {
 6     class Program
 7     {
 8         static void Main(string[] args)
 9         {
10             Task<string[]> parent = new Task<string[]>(state =>
11             {
12                 Console.WriteLine(state);
13                 string[] result = new string[2];
14                 // 创建并启动子任务
15                 new Task(() => { result[0] = "我是子任务1。"; }, TaskCreationOptions.AttachedToParent).Start();
16                 new Task(() => { result[1] = "我是子任务2。"; }, TaskCreationOptions.AttachedToParent).Start();
17                 return result;
18             }, "我是父任务,并在我的处理过程中创建多个子任务,所有子任务完成以后我才会结束执行。");
19             // 任务处理完成后执行的操作
20             parent.ContinueWith(t => { Array.ForEach(t.Result, r => Console.WriteLine(r)); });
21             // 启动父任务
22             parent.Start();
23             // 等待任务结束 Wait 只能等待父线程结束,没办法等到父线程的 ContinueWith 结束
24             parent.Wait();
25             Console.ReadLine();   
26         }
27     }
28 }     

执行结果:

 动态并行(TaskCreationOptions.AttachedToParent)父任务等待所有子任务完成后,整个任务才算完成

 1 using System;
 2 using System.Collections.Concurrent;
 3 using System.Threading;
 4 using System.Threading.Tasks;
 5 
 6 namespace ContiuneWithDemo
 7 {
 8     class Program
 9     {
10         static void Main(string[] args)
11         {
12             Node root = GetNode();
13             DisplayTree(root);
14             Console.ReadKey();
15          }
16  
17         class Node
18         {
19             public Node Left { get; set; }
20             public Node Right { get; set; }
21             public string Text { get; set; }
22         }
23 
24         static Node GetNode()
25         {
26             Node root = new Node
27             {
28                 Left = new Node
29                 {
30                     Left = new Node
31                     {
32                         Text = "L-L"
33                     },
34                     Right = new Node
35                     {
36                         Text = "L-R"
37                     },
38                     Text = "L"
39                 },
40                 Right = new Node
41                 {
42                     Left = new Node
43                     {
44                         Text = "R-L"
45                     },
46                     Right = new Node
47                     {
48                         Text = "R-R"
49                     },
50                     Text = "R"
51                 },
52                 Text = "Root"
53             };
54             return root;
55         }
56 
57         static void DisplayTree(Node root)
58         {
59             var task = Task.Factory.StartNew(()=>DisplayNode(root),
60                 CancellationToken.None,
61                 TaskCreationOptions.None,
62                 TaskScheduler.Default);
63             task.Wait();
64         }
65 
66         static void DisplayNode(Node current)
67         {
68             if (current.Left != null)
69             {
70                 Task.Factory.StartNew(()=>DisplayNode(current.Left),
71                     CancellationToken.None,
72                     TaskCreationOptions.AttachedToParent,
73                     TaskScheduler.Default);
74             }
75             if (current.Right != null)
76             {
77                 Task.Factory.StartNew(()=>DisplayNode(current.Right),
78                     CancellationToken.None,
79                     TaskCreationOptions.AttachedToParent,
80                     TaskScheduler.Default);
81             }
82             Console.WriteLine("当前节点的值为{0};处理的 ThreadId = {1}", current.Text, Thread.CurrentThread.ManagedThreadId);
83         }
84     }
85 }

 

执行结果:

2.3 取消任务 CancellationTokenSource

 1 using System;
 2 using System.Threading;
 3 using System.Threading.Tasks;
 4 
 5 namespace CanellationTokenSourceDemo
 6 {
 7     class Program
 8     {
 9         static void Main(string[] args)
10         {
11             var cts = new CancellationTokenSource();
12             var longTask = new Task<int>(()=>TaskMethod("Task 1", 10, cts.Token), cts.Token);
13             Console.WriteLine(longTask.Status);
14             cts.Cancel();
15             Console.WriteLine(longTask.Status);
16             Console.WriteLine("First task has been canelled before execution.");
17             cts = new CancellationTokenSource();
18             longTask = new Task<int>(()=>TaskMethod("Task 2", 10, cts.Token), cts.Token);
19             longTask.Start();
20             for (int i = 0; i < 5; i++)
21             {
22                 Thread.Sleep(TimeSpan.FromSeconds(0.5));
23                 Console.WriteLine(longTask.Status);
24             }
25             cts.Cancel();
26             for (int i = 0; i < 5; i++)
27             {
28                 Thread.Sleep(TimeSpan.FromSeconds(0.5));
29                 Console.WriteLine(longTask.Status);
30             }
31             Console.WriteLine("A task has been completed with result {0}.", longTask.Result);
32             Console.ReadKey();
33         }
34 
35         private static int TaskMethod(string name, int seconds, CancellationToken token)
36         {
37             Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
38                 name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
39             for (int i = 0; i < seconds; i++)
40             {
41                 Thread.Sleep(TimeSpan.FromSeconds(1));
42                 if (token.IsCancellationRequested)
43                 {
44                     return -1;
45                 }
46             }
47             return 42 * seconds;
48         }
49     }
50 }

 

posted @ 2021-08-13 17:33  llkj  阅读(1519)  评论(0编辑  收藏  举报