C# Task詳解


1、Task的優勢

  ThreadPool相比Thread來說具備了很多優勢,但是ThreadPool卻又存在一些使用上的不方便。比如:
  ◆ ThreadPool不支持線程的取消、完成、失敗通知等交互性操作;
  ◆ ThreadPool不支持線程執行的先后次序;
  以往,如果開發者要實現上述功能,需要完成很多額外的工作,現在,FCL中提供了一個功能更強大的概念:Task。Task在線程池的基礎上進行了優化,並提供了更多的API。在FCL4.0中,如果我們要編寫多線程程序,Task顯然已經優於傳統的方式。
  以下是一個簡單的任務示例:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Task t = new Task(() =>
            {
                Console.WriteLine("任務開始工作……");
                //模擬工作過程
                Thread.Sleep(5000);
            });
            t.Start();
            t.ContinueWith((task) =>
            {
                Console.WriteLine("任務完成,完成時候的狀態為:");
                Console.WriteLine("IsCanceled={0}\tIsCompleted={1}\tIsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted);
            });
            Console.ReadKey();
        }
    }
}

2、Task的用法

  2.1、創建任務

  (一)無返回值的方式
  方式1:

  var t1 = new Task(() => TaskMethod("Task 1"));
  t1.Start();
  Task.WaitAll(t1);//等待所有任務結束 
  注:任務的狀態:
  Start之前為:Created
  Start之后為:WaitingToRun 

方式2:

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

 方式3:

  Task.Factory.StartNew(() => TaskMethod("Task 3")); 直接異步的方法 
  //或者
  var t3=Task.Factory.StartNew(() => TaskMethod("Task 3"));
  Task.WaitAll(t3);//等待所有任務結束
  //任務的狀態:
  Start之前為:Running
  Start之后為:Running
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var t1 = new Task(() => TaskMethod("Task 1"));
            var t2 = new Task(() => TaskMethod("Task 2"));
            t2.Start();
            t1.Start();
            Task.WaitAll(t1, t2);
            Task.Run(() => TaskMethod("Task 3"));
            Task.Factory.StartNew(() => TaskMethod("Task 4"));
            //標記為長時間運行任務,則任務不會使用線程池,而在單獨的線程中運行。
            Task.Factory.StartNew(() => TaskMethod("Task 5"), TaskCreationOptions.LongRunning);

            #region 常規的使用方式
            Console.WriteLine("主線程執行業務處理.");
            //創建任務
            Task task = new Task(() =>
            {
                Console.WriteLine("使用System.Threading.Tasks.Task執行異步操作.");
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine(i);
                }
            });
            //啟動任務,並安排到當前任務隊列線程中執行任務(System.Threading.Tasks.TaskScheduler)
            task.Start();
            Console.WriteLine("主線程執行其他處理");
            task.Wait();
            #endregion

            Thread.Sleep(TimeSpan.FromSeconds(1));
            Console.ReadLine();
        }

        static void TaskMethod(string name)
        {
            Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
                name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
        }
    }
}
復制代碼
  async/await的實現方式:


復制代碼
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        async static void AsyncFunction()
        {
            await Task.Delay(1);
            Console.WriteLine("使用System.Threading.Tasks.Task執行異步操作.");
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(string.Format("AsyncFunction:i={0}", i));
            }
        }

        public static void Main()
        {
            Console.WriteLine("主線程執行業務處理.");
            AsyncFunction();
            Console.WriteLine("主線程執行其他處理");
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(string.Format("Main:i={0}", i));
            }
            Console.ReadLine();
        }
    }
}

 (二)帶返回值的方式
  方式4:

 Task<int> task = CreateTask("Task 1");
  task.Start(); 
  int result = task.Result;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static Task<int> CreateTask(string name)
        {
            return new Task<int>(() => TaskMethod(name));
        }

        static void Main(string[] args)
        {
            TaskMethod("Main Thread Task");
            Task<int> task = CreateTask("Task 1");
            task.Start();
            int result = task.Result;
            Console.WriteLine("Task 1 Result is: {0}", result);

            task = CreateTask("Task 2");
            //該任務會運行在主線程中
            task.RunSynchronously();
            result = task.Result;
            Console.WriteLine("Task 2 Result is: {0}", result);

            task = CreateTask("Task 3");
            Console.WriteLine(task.Status);
            task.Start();

            while (!task.IsCompleted)
            {
                Console.WriteLine(task.Status);
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
            }

            Console.WriteLine(task.Status);
            result = task.Result;
            Console.WriteLine("Task 3 Result is: {0}", result);

            #region 常規使用方式
            //創建任務
            Task<int> getsumtask = new Task<int>(() => Getsum());
            //啟動任務,並安排到當前任務隊列線程中執行任務(System.Threading.Tasks.TaskScheduler)
            getsumtask.Start();
            Console.WriteLine("主線程執行其他處理");
            //等待任務的完成執行過程。
            getsumtask.Wait();
            //獲得任務的執行結果
            Console.WriteLine("任務執行結果:{0}", getsumtask.Result.ToString());
            #endregion
        }

        static int TaskMethod(string name)
        {
            Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
                name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
            Thread.Sleep(TimeSpan.FromSeconds(2));
            return 42;
        }

        static int Getsum()
        {
            int sum = 0;
            Console.WriteLine("使用Task執行異步操作.");
            for (int i = 0; i < 100; i++)
            {
                sum += i;
            }
            return sum;
        }
    }
}
  async/await的實現:
using
System; using System.Threading.Tasks; namespace ConsoleApp1 { class Program { public static void Main() { var ret1 = AsyncGetsum(); Console.WriteLine("主線程執行其他處理"); for (int i = 1; i <= 3; i++) Console.WriteLine("Call Main()"); int result = ret1.Result; //阻塞主線程 Console.WriteLine("任務執行結果:{0}", result); } async static Task<int> AsyncGetsum() { await Task.Delay(1); int sum = 0; Console.WriteLine("使用Task執行異步操作."); for (int i = 0; i < 100; i++) { sum += i; } return sum; } } }

 2.2、組合任務.ContinueWith

   簡單Demo:

using System;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        public static void Main()
        {
            //創建一個任務
            Task<int> task = new Task<int>(() =>
            {
                int sum = 0;
                Console.WriteLine("使用Task執行異步操作.");
                for (int i = 0; i < 100; i++)
                {
                    sum += i;
                }
                return sum;
            });
            //啟動任務,並安排到當前任務隊列線程中執行任務(System.Threading.Tasks.TaskScheduler)
            task.Start();
            Console.WriteLine("主線程執行其他處理");
            //任務完成時執行處理。
            Task cwt = task.ContinueWith(t =>
            {
                Console.WriteLine("任務完成后的執行結果:{0}", t.Result.ToString());
            });
            task.Wait();
            cwt.Wait();
        }
    }
}

 任務的串行:

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            ConcurrentStack<int> stack = new ConcurrentStack<int>();

            //t1先串行
            var t1 = Task.Factory.StartNew(() =>
            {
                stack.Push(1);
                stack.Push(2);
            });

            //t2,t3並行執行
            var t2 = t1.ContinueWith(t =>
            {
                int result;
                stack.TryPop(out result);
                Console.WriteLine("Task t2 result={0},Thread id {1}", result, Thread.CurrentThread.ManagedThreadId);
            });

            //t2,t3並行執行
            var t3 = t1.ContinueWith(t =>
            {
                int result;
                stack.TryPop(out result);
                Console.WriteLine("Task t3 result={0},Thread id {1}", result, Thread.CurrentThread.ManagedThreadId);
            });

            //等待t2和t3執行完
            Task.WaitAll(t2, t3);

            //t7串行執行
            var t4 = Task.Factory.StartNew(() =>
            {
                Console.WriteLine("當前集合元素個數:{0},Thread id {1}", stack.Count, Thread.CurrentThread.ManagedThreadId);
            });
            t4.Wait();
        }
    }
}

子任務:

using System;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        public static void Main()
        {
            Task<string[]> parent = new Task<string[]>(state =>
            {
                Console.WriteLine(state);
                string[] result = new string[2];
                //創建並啟動子任務
                new Task(() => { result[0] = "我是子任務1。"; }, TaskCreationOptions.AttachedToParent).Start();
                new Task(() => { result[1] = "我是子任務2。"; }, TaskCreationOptions.AttachedToParent).Start();
                return result;
            }, "我是父任務,並在我的處理過程中創建多個子任務,所有子任務完成以后我才會結束執行。");
            //任務處理完成后執行的操作
            parent.ContinueWith(t =>
            {
                Array.ForEach(t.Result, r => Console.WriteLine(r));
            });
            //啟動父任務
            parent.Start();
            //等待任務結束 Wait只能等待父線程結束,沒辦法等到父線程的ContinueWith結束
            //parent.Wait();
            Console.ReadLine();

        }
    }
}

動態並行(TaskCreationOptions.AttachedToParent) 父任務等待所有子任務完成后 整個任務才算完成

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Node
    {
        public Node Left { get; set; }
        public Node Right { get; set; }
        public string Text { get; set; }
    }


    class Program
    {
        static Node GetNode()
        {
            Node root = new Node
            {
                Left = new Node
                {
                    Left = new Node
                    {
                        Text = "L-L"
                    },
                    Right = new Node
                    {
                        Text = "L-R"
                    },
                    Text = "L"
                },
                Right = new Node
                {
                    Left = new Node
                    {
                        Text = "R-L"
                    },
                    Right = new Node
                    {
                        Text = "R-R"
                    },
                    Text = "R"
                },
                Text = "Root"
            };
            return root;
        }

        static void Main(string[] args)
        {
            Node root = GetNode();
            DisplayTree(root);
        }

        static void DisplayTree(Node root)
        {
            var task = Task.Factory.StartNew(() => DisplayNode(root),
                                            CancellationToken.None,
                                            TaskCreationOptions.None,
                                            TaskScheduler.Default);
            task.Wait();
        }

        static void DisplayNode(Node current)
        {

            if (current.Left != null)
                Task.Factory.StartNew(() => DisplayNode(current.Left),
                                            CancellationToken.None,
                                            TaskCreationOptions.AttachedToParent,
                                            TaskScheduler.Default);
            if (current.Right != null)
                Task.Factory.StartNew(() => DisplayNode(current.Right),
                                            CancellationToken.None,
                                            TaskCreationOptions.AttachedToParent,
                                            TaskScheduler.Default);
            Console.WriteLine("當前節點的值為{0};處理的ThreadId={1}", current.Text, Thread.CurrentThread.ManagedThreadId);
        }
    }
}

2.3、取消任務 CancellationTokenSource

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        private static int TaskMethod(string name, int seconds, CancellationToken token)
        {
            Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
                name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
            for (int i = 0; i < seconds; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(1));
                if (token.IsCancellationRequested) return -1;
            }
            return 42 * seconds;
        }

        private static void Main(string[] args)
        {
            var cts = new CancellationTokenSource();
            var longTask = new Task<int>(() => TaskMethod("Task 1", 10, cts.Token), cts.Token);
            Console.WriteLine(longTask.Status);
            cts.Cancel();
            Console.WriteLine(longTask.Status);
            Console.WriteLine("First task has been cancelled before execution");
            cts = new CancellationTokenSource();
            longTask = new Task<int>(() => TaskMethod("Task 2", 10, cts.Token), cts.Token);
            longTask.Start();
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
                Console.WriteLine(longTask.Status);
            }
            cts.Cancel();
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
                Console.WriteLine(longTask.Status);
            }

            Console.WriteLine("A task has been completed with result {0}.", longTask.Result);
        }
    }
}

2.4、處理任務中的異常

  單個任務:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static int TaskMethod(string name, int seconds)
        {
            Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
                name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
            Thread.Sleep(TimeSpan.FromSeconds(seconds));
            throw new Exception("Boom!");
            return 42 * seconds;
        }

        static void Main(string[] args)
        {
            try
            {
                Task<int> task = Task.Run(() => TaskMethod("Task 2", 2));
                int result = task.GetAwaiter().GetResult();
                Console.WriteLine("Result: {0}", result);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Task 2 Exception caught: {0}", ex.Message);
            }
            Console.WriteLine("----------------------------------------------");
            Console.WriteLine();
        }
    }
}

多個任務:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static int TaskMethod(string name, int seconds)
        {
            Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
                name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
            Thread.Sleep(TimeSpan.FromSeconds(seconds));
            throw new Exception(string.Format("Task {0} Boom!", name));
            return 42 * seconds;
        }


        public static void Main(string[] args)
        {
            try
            {
                var t1 = new Task<int>(() => TaskMethod("Task 3", 3));
                var t2 = new Task<int>(() => TaskMethod("Task 4", 2));
                var complexTask = Task.WhenAll(t1, t2);
                var exceptionHandler = complexTask.ContinueWith(t =>
                        Console.WriteLine("Result: {0}", t.Result),
                        TaskContinuationOptions.OnlyOnFaulted
                    );
                t1.Start();
                t2.Start();
                Task.WaitAll(t1, t2);
            }
            catch (AggregateException ex)
            {
                ex.Handle(exception =>
                {
                    Console.WriteLine(exception.Message);
                    return true;
                });
            }
        }
    }
}

    async/await的方式:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static async Task ThrowNotImplementedExceptionAsync()
        {
            throw new NotImplementedException();
        }

        static async Task ThrowInvalidOperationExceptionAsync()
        {
            throw new InvalidOperationException();
        }

        static async Task Normal()
        {
            await Fun();
        }

        static Task Fun()
        {
            return Task.Run(() =>
            {
                for (int i = 1; i <= 10; i++)
                {
                    Console.WriteLine("i={0}", i);
                    Thread.Sleep(200);
                }
            });
        }

        static async Task ObserveOneExceptionAsync()
        {
            var task1 = ThrowNotImplementedExceptionAsync();
            var task2 = ThrowInvalidOperationExceptionAsync();
            var task3 = Normal();


            try
            {
                //異步的方式
                Task allTasks = Task.WhenAll(task1, task2, task3);
                await allTasks;
                //同步的方式
                //Task.WaitAll(task1, task2, task3);
            }
            catch (NotImplementedException ex)
            {
                Console.WriteLine("task1 任務報錯!");
            }
            catch (InvalidOperationException ex)
            {
                Console.WriteLine("task2 任務報錯!");
            }
            catch (Exception ex)
            {
                Console.WriteLine("任務報錯!");
            }

        }

        public static void Main()
        {
            Task task = ObserveOneExceptionAsync();
            Console.WriteLine("主線程繼續運行........");
            task.Wait();
        }
    }
}

2.5、Task.FromResult的應用

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static IDictionary<string, string> cache = new Dictionary<string, string>()
        {
            {"0001","A"},
            {"0002","B"},
            {"0003","C"},
            {"0004","D"},
            {"0005","E"},
            {"0006","F"},
        };

        public static void Main()
        {
            Task<string> task = GetValueFromCache("0006");
            Console.WriteLine("主程序繼續執行。。。。");
            string result = task.Result;
            Console.WriteLine("result={0}", result);

        }

        private static Task<string> GetValueFromCache(string key)
        {
            Console.WriteLine("GetValueFromCache開始執行。。。。");
            string result = string.Empty;
            //Task.Delay(5000);
            Thread.Sleep(5000);
            Console.WriteLine("GetValueFromCache繼續執行。。。。");
            if (cache.TryGetValue(key, out result))
            {
                return Task.FromResult(result);
            }
            return Task.FromResult("");
        }

    }
}

 2.6、使用IProgress實現異步編程的進程通知

  IProgress<in T>只提供了一個方法void Report(T value),通過Report方法把一個T類型的值報告給IProgress,然后IProgress<in T>的實現類Progress<in T>的構造函數接收類型為Action<T>的形參,通過這個委托讓進度顯示在UI界面中。

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void DoProcessing(IProgress<int> progress)
        {
            for (int i = 0; i <= 100; ++i)
            {
                Thread.Sleep(100);
                if (progress != null)
                {
                    progress.Report(i);
                }
            }
        }

        static async Task Display()
        {
            //當前線程
            var progress = new Progress<int>(percent =>
            {
                Console.Clear();
                Console.Write("{0}%", percent);
            });
            //線程池線程
            await Task.Run(() => DoProcessing(progress));
            Console.WriteLine("");
            Console.WriteLine("結束");
        }

        public static void Main()
        {
            Task task = Display();
            task.Wait();
        }
    }
}

 2.7、Factory.FromAsync的應用 (簡APM模式(委托)轉換為任務)(BeginXXX和EndXXX)

  帶回調方式的

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        private delegate string AsynchronousTask(string threadName);

        private static string Test(string threadName)
        {
            Console.WriteLine("Starting...");
            Console.WriteLine("Is thread pool thread: {0}", Thread.CurrentThread.IsThreadPoolThread);
            Thread.Sleep(TimeSpan.FromSeconds(2));
            Thread.CurrentThread.Name = threadName;
            return string.Format("Thread name: {0}", Thread.CurrentThread.Name);
        }

        private static void Callback(IAsyncResult ar)
        {
            Console.WriteLine("Starting a callback...");
            Console.WriteLine("State passed to a callbak: {0}", ar.AsyncState);
            Console.WriteLine("Is thread pool thread: {0}", Thread.CurrentThread.IsThreadPoolThread);
            Console.WriteLine("Thread pool worker thread id: {0}", Thread.CurrentThread.ManagedThreadId);
        }

        //執行的流程是 先執行Test--->Callback--->task.ContinueWith
        static void Main(string[] args)
        {
            AsynchronousTask d = Test;
            Console.WriteLine("Option 1");
            Task<string> task = Task<string>.Factory.FromAsync(
                d.BeginInvoke("AsyncTaskThread", Callback, "a delegate asynchronous call"), d.EndInvoke);

            task.ContinueWith(t => Console.WriteLine("Callback is finished, now running a continuation! Result: {0}",
                t.Result));

            while (!task.IsCompleted)
            {
                Console.WriteLine(task.Status);
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
            }
            Console.WriteLine(task.Status);

        }
    }
}

不帶回調方式的

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        private delegate string AsynchronousTask(string threadName);

        private static string Test(string threadName)
        {
            Console.WriteLine("Starting...");
            Console.WriteLine("Is thread pool thread: {0}", Thread.CurrentThread.IsThreadPoolThread);
            Thread.Sleep(TimeSpan.FromSeconds(2));
            Thread.CurrentThread.Name = threadName;
            return string.Format("Thread name: {0}", Thread.CurrentThread.Name);
        }

        //執行的流程是 先執行Test--->task.ContinueWith
        static void Main(string[] args)
        {
            AsynchronousTask d = Test;
            Task<string> task = Task<string>.Factory.FromAsync(
                d.BeginInvoke, d.EndInvoke, "AsyncTaskThread", "a delegate asynchronous call");
            task.ContinueWith(t => Console.WriteLine("Task is completed, now running a continuation! Result: {0}",
                t.Result));
            while (!task.IsCompleted)
            {
                Console.WriteLine(task.Status);
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
            }
            Console.WriteLine(task.Status);

        }
    }
}

參考2

簡介:

Task 對象是一種的中心思想基於任務的異步模式首次引入.NET Framework 4 中。 因為由執行工作Task對象通常以異步方式執行線程池線程上而不是以同步方式在主應用程序線程中,可以使用Status屬性,並將IsCanceled, IsCompleted,和IsFaulted屬性,以確定任務的狀態。


一.Task的創建

1.創建Task類

(1)

1
2
3
4
5
Task task =  new  Task(() =>
{
     Console.WriteLine( "hello world!" );
});
task.Start(); 

(2)

1
2
3
4
new  Task(() =>
{
     Console.WriteLine( "hello world!" );
}).Start();

(3)帶參數

1
2
3
4
new  Task(x =>
{
     Console.WriteLine(x.ToString());
},  "hello world!" ).Start();

(4)帶返回值

1
2
3
4
5
6
Task< string > t =  new  Task< string >(x =>
{
     return  x.ToString();
},  "hello world!" );
t.Start();
Console.WriteLine(t.Result);

  

2.Task.Factory.StartNew

(1)

1
2
3
4
Task.Factory.StartNew(() =>
{
     Console.WriteLine( "hello world!" );
});

(2)帶參數

1
2
3
4
Task.Factory.StartNew(x =>
{
     Console.WriteLine(x.ToString());
},  "hello world!" );

  

(3)帶返回值

1
2
3
4
Task< string > t = Task.Factory.StartNew< string >(() =>
{
     return  "hello world!" ;
});<br>        Console.WriteLine(t.Result);

  

3.Task.Run

1
2
3
4
Task.Run(() =>
{
     Console.WriteLine( "hello world!" );
});

4.TaskStatus

1
2
3
4
5
6
7
8
Created = 0,  //該任務已初始化,但尚未被計划。
WaitingForActivation = 1,  //該任務正在等待 .NET Framework 基礎結構在內部將其激活並進行計划。
WaitingToRun = 2, //該任務已被計划執行,但尚未開始執行。
Running = 3,  //該任務正在運行,但尚未完成。
WaitingForChildrenToComplete = 4, //該任務已完成執行,正在隱式等待附加的子任務完成。
RanToCompletion = 5, //已成功完成執行的任務。
Canceled = 6,  //該任務已通過對其自身的 CancellationToken 引發 OperationCanceledException 對取消進行了確認,此時該標記處於已發送信號狀態;或者在該任務開始執行之前,已向該任務的CancellationToken 發出了信號。 有關詳細信息,請參閱任務取消。
Faulted = 7  //由於未處理異常的原因而完成的任務。

  

二. TaskCreationOptions

 Task.Factory.StartNew和創建Task類可以帶TaskCreationOptions參數而Task.Run不可以帶

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//
// 摘要:
//     指定應使用默認行為。
None = 0,
//
// 摘要:
//     提示 System.Threading.Tasks.TaskScheduler 以一種盡可能公平的方式安排任務,這意味着較早安排的任務將更可能較早運行,而較晚安排運行的任務將更可能較晚運行。
PreferFairness = 1,
//
// 摘要:
//     指定任務將是長時間運行的、粗粒度的操作,涉及比細化的系統更少、更大的組件。 它會向 System.Threading.Tasks.TaskScheduler
//     提示,過度訂閱可能是合理的。 可以通過過度訂閱創建比可用硬件線程數更多的線程。 它還將提示任務計划程序:該任務需要附加線程,以使任務不阻塞本地線程池隊列中其他線程或工作項的向前推動。
LongRunning = 2,
//
// 摘要:
//     指定將任務附加到任務層次結構中的某個父級。 默認情況下,子任務(即由外部任務創建的內部任務)將獨立於其父任務執行。 可以使用 System.Threading.Tasks.TaskContinuationOptions.AttachedToParent
//     選項以便將父任務和子任務同步。 請注意,如果使用 System.Threading.Tasks.TaskCreationOptions.DenyChildAttach
//     選項配置父任務,則子任務中的 System.Threading.Tasks.TaskCreationOptions.AttachedToParent 選項不起作用,並且子任務將作為分離的子任務執行。
//     有關詳細信息,請參閱附加和分離的子任務。
AttachedToParent = 4,
//
// 摘要:
//     指定任何嘗試作為附加的子任務執行(即,使用 System.Threading.Tasks.TaskCreationOptions.AttachedToParent
//     選項創建)的子任務都無法附加到父任務,會改成作為分離的子任務執行。 有關詳細信息,請參閱附加和分離的子任務。
DenyChildAttach = 8,
//
// 摘要:
//     防止環境計划程序被視為已創建任務的當前計划程序。 這意味着像 StartNew 或 ContinueWith 創建任務的執行操作將被視為 System.Threading.Tasks.TaskScheduler.Default
//     當前計划程序。
HideScheduler = 16

1. LongRunning

任務是長時間任務,就需要用LongRunning,可能會創建一個非線程池線程來執行該任務,防止阻塞線程池隊列中的其他線程

1
2
3
4
5
6
7
8
9
10
11
12
private  static  void  fun8()
{
     Task.Factory.StartNew(() =>
     {
         Console.WriteLine($ "task1.線程 id {Thread.CurrentThread.ManagedThreadId}. 是否為線程池線程: {Thread.CurrentThread.IsThreadPoolThread}" );
     });
 
     Task.Factory.StartNew(() =>
     {
         Console.WriteLine($ "task2.線程 id {Thread.CurrentThread.ManagedThreadId}. 是否為線程池線程: {Thread.CurrentThread.IsThreadPoolThread}" );
     }, TaskCreationOptions.LongRunning);
}

 運行結果:

 2. 父子任務(AttachedToParent,DenyChildAttach)

AttachedToParent:將子任務附加到父任務上,表現為:附加到父任務上的所有子任務都結束,父任務才結束

DenyChildAttach:不允許子任務附加到父任務上

(1)子任務不附加到父任務

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private  static  void  fun5()
{
     Task t = Task.Factory.StartNew(() =>
     {
         Console.WriteLine( "parent" );
 
         Task.Factory.StartNew(() =>
         {
             Thread.Sleep(1000);
             Console.WriteLine( "child" );
         });
     });
     t.ContinueWith(x =>
     {
         Console.WriteLine( "parent over" );
     });
}

 運行結果:

(2)子任務附加到父任務上,使用AttachedToParent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private  static  void  fun6()
{
     Task t = Task.Factory.StartNew(() =>
     {
         Console.WriteLine( "parent" );
 
         Task.Factory.StartNew(() =>
         {
             Thread.Sleep(1000);
             Console.WriteLine( "child" );
         },TaskCreationOptions.AttachedToParent);
     });
     t.ContinueWith(x =>
     {
         Console.WriteLine( "parent over" );
     });
}

 運行結果:

(3)拒絕子任務附加到父任務上,使用DenyChildAttach

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private  static  void  fun7()
{
     Task t = Task.Factory.StartNew(() =>
     {
         Console.WriteLine( "parent" );
 
         Task.Factory.StartNew(() =>
         {
             Thread.Sleep(1000);
             Console.WriteLine( "child" );
         }, TaskCreationOptions.AttachedToParent);
     }, TaskCreationOptions.DenyChildAttach);
     t.ContinueWith(x =>
     {
         Console.WriteLine( "parent over" );
     });
}

  運行結果:

 

 三.CancellationToken 取消任務

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
private  static  void  fun4()
{
     CancellationTokenSource cancellationTokenSource =  new  CancellationTokenSource();
     CancellationToken Token = cancellationTokenSource.Token;
     //結束任務回調
     Token.Register(() =>
     {
         Console.WriteLine( "canceled" );
     });
 
     Task.Factory.StartNew(() =>
     {
         try
         {
             while  ( true )
             {
                 Console.WriteLine( "hello world!" );
                 Thread.Sleep(100);
                 Token.ThrowIfCancellationRequested();
             }
         }
         catch  (OperationCanceledException ocex)
         {
         }
         catch  (ObjectDisposedException odex)
         {
         }
         catch  (Exception ex)
         {
         }
 
     }, Token);
 
     Thread.Sleep(1000);
 
     cancellationTokenSource.Cancel();
}

 

執行結果:

當執行cancellationTokenSource.Cancel()后,任務進行到Token.ThrowIfCancellationRequested()代碼后,throw出OperationCanceledException異常,才結束任務並執行cancel回調


 四.方法

Wait 等待 System.Threading.Tasks.Task 完成執行過程
WaitAll  等待提供的所有 System.Threading.Tasks.Task 對象完成執行過程
WaitAny  等待提供的任一 System.Threading.Tasks.Task 對象完成執行過程
WhenAll  創建一個任務,該任務將在所有 System.Threading.Tasks.Task 對象都完成時完成
WhenAny  任何提供的任務已完成時,創建將完成的任務
ContinueWith  創建一個在目標 System.Threading.Tasks.Task 完成時異步執行的延續任務

 

1 Wait

阻塞當前線程,等待任務執行完成

1
2
3
4
5
6
7
8
9
10
//等待 System.Threading.Tasks.Task 在指定的毫秒數內完成執行。
public  bool  Wait( int  millisecondsTimeout);
//等待 System.Threading.Tasks.Task 完成執行過程。 如果在任務完成之前取消標記已取消,等待將終止。
public  void  Wait(CancellationToken cancellationToken);
//等待 System.Threading.Tasks.Task 完成執行過程。 如果在任務完成之前超時間隔結束或取消標記已取消,等待將終止。      
public  bool  Wait( int  millisecondsTimeout, CancellationToken cancellationToken);      
//等待 System.Threading.Tasks.Task 完成執行過程。
public  void  Wait();
//等待 System.Threading.Tasks.Task 在指定的時間間隔內完成執行。
public  bool  Wait(TimeSpan timeout);

使用方式:

1
2
3
t.Wait(); //無限等待
t.Wait(100); //等待100ms
t.Wait(TimeSpan.FromMilliseconds(1500)); //等待1500ms

例:

1
2
3
4
5
6
7
8
9
10
private  static  void  fun9()
{
     Task t = Task.Factory.StartNew(() =>
     {
         Thread.Sleep(1000);
         Console.WriteLine( "task" );
     });
     t.Wait();
     Console.WriteLine( "main" );
}

 運行結果:

2.WaitAll 

阻塞當前線程,等待所有任務執行完成

1
2
3
4
5
6
7
8
9
10
//等待提供的所有 System.Threading.Tasks.Task 對象完成執行過程。
public  static  void  WaitAll( params  Task[] tasks);
//等待所有提供的可取消 System.Threading.Tasks.Task 對象在指定的時間間隔內完成執行。
public  static  bool  WaitAll(Task[] tasks, TimeSpan timeout);
//等待所有提供的 System.Threading.Tasks.Task 在指定的毫秒數內完成執行。
public  static  bool  WaitAll(Task[] tasks,  int  millisecondsTimeout);
//等待提供的所有 System.Threading.Tasks.Task 對象完成執行過程(除非取消等待)。
public  static  void  WaitAll(Task[] tasks, CancellationToken cancellationToken);
//等待提供的所有 System.Threading.Tasks.Task 對象在指定的毫秒數內完成執行,或等到取消等待。
public  static  bool  WaitAll(Task[] tasks,  int  millisecondsTimeout, CancellationToken cancellationToken);

  使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private  static  void  fun10()
{
     Task t1 = Task.Factory.StartNew(() =>
     {
         Thread.Sleep(100);
         Console.WriteLine( "task1" );
     });
 
     Task t2 = Task.Factory.StartNew(() =>
     {
         Thread.Sleep(150);
         Console.WriteLine( "task2" );
     });
 
     //Task.WaitAll(t1, t2);
     //Task.WaitAll(new Task[] { t1, t2 }, 200);
     Task.WaitAll( new  Task[] { t1, t2 }, TimeSpan.FromMilliseconds(200));
     Console.WriteLine( "main" );
}

  

 運行結果:

3.WaitAny

阻塞當前線程,等待任一任務執行完成

1
2
3
4
5
6
7
8
9
10
//等待提供的任一 System.Threading.Tasks.Task 對象完成執行過程。
public  static  int  WaitAny( params  Task[] tasks);
//等待任何提供的 System.Threading.Tasks.Task 對象在指定的時間間隔內完成執行。
public  static  int  WaitAny(Task[] tasks, TimeSpan timeout);
//等待任何提供的 System.Threading.Tasks.Task 對象在指定的毫秒數內完成執行。
public  static  int  WaitAny(Task[] tasks,  int  millisecondsTimeout);
//等待提供的任何 System.Threading.Tasks.Task 對象完成執行過程(除非取消等待)。
public  static  int  WaitAny(Task[] tasks, CancellationToken cancellationToken);
//等待提供的任何 System.Threading.Tasks.Task 對象在指定的毫秒數內完成執行,或等到取消標記取消。
public  static  int  WaitAny(Task[] tasks,  int  millisecondsTimeout, CancellationToken cancellationToken);

 使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private  static  void  fun11()
{
     Task t1 = Task.Factory.StartNew(() =>
     {
         Thread.Sleep(100);
         Console.WriteLine( "task1" );
     });
 
     Task t2 = Task.Factory.StartNew(() =>
     {
         Thread.Sleep(150);
         Console.WriteLine( "task2" );
     });
 
     //Task.WaitAny(t1, t2);
     //Task.WaitAny(new Task[] { t1, t2 }, 200);
     Task.WaitAny( new  Task[] { t1, t2 }, TimeSpan.FromMilliseconds(200));
     Console.WriteLine( "main" );
}

 結果:

4.WhenAll

不阻塞當前線程,等待所有任務執行完成后,可以進行ContinueWith

1
2
3
4
5
6
7
8
//創建一個任務,該任務將在可枚舉集合中的所有 System.Threading.Tasks.Task 對象都完成時完成。
public  static  Task WhenAll(IEnumerable<Task> tasks);
//創建一個任務,該任務將在數組中的所有 System.Threading.Tasks.Task 對象都完成時完成。
public  static  Task WhenAll( params  Task[] tasks);
//創建一個任務,該任務將在可枚舉集合中的所有 System.Threading.Tasks.Task`1 對象都完成時完成。
public  static  Task<TResult[]> WhenAll<TResult>(IEnumerable<Task<TResult>> tasks);
//創建一個任務,該任務將在數組中的所有 System.Threading.Tasks.Task`1 對象都完成時完成。
public  static  Task<TResult[]> WhenAll<TResult>( params  Task<TResult>[] tasks);

  使用:

(1)不帶返回值 

1
public  static  Task WhenAll( params  Task[] tasks);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private  static  void  fun12()
{
     Task t1 = Task.Factory.StartNew(() =>
     {
         Thread.Sleep(100);
         Console.WriteLine( "task1" );
     });
 
     Task t2 = Task.Factory.StartNew(() =>
     {
         Thread.Sleep(150);
         Console.WriteLine( "task2" );
     });
 
     Task.WhenAll(t1, t2).ContinueWith(t =>
     {
         Console.WriteLine( "WhenAll" );
     });
 
     Console.WriteLine( "main" );
}

  執行結果:

(2)帶返回值

1
public  static  Task<TResult[]> WhenAll<TResult>( params  Task<TResult>[] tasks);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private  static  void  fun13()
{
     Task< string > t1 = Task.Factory.StartNew(() =>
     {
         Thread.Sleep(100);
         Console.WriteLine( "task1" );
         return  "task1" ;
     });
 
     Task< string > t2 = Task.Factory.StartNew< string >(() =>
     {
         Thread.Sleep(150);
         Console.WriteLine( "task2" );
         return  "task2" ;
     });
 
     Task.WhenAll( new  Task< string >[] { t1, t2 }).ContinueWith(t =>
     {
         string  s =  "WhenAll:" ;
         foreach  ( string  item  in  t.Result)
         {
             s += item;
         }
         Console.WriteLine(s);
     });
 
     Console.WriteLine( "main" );
}

  執行結果:

5.WhenAny

1
2
3
4
5
6
7
8
//任何提供的任務已完成時,創建將完成的任務。
public  static  Task<Task> WhenAny( params  Task[] tasks);
//任何提供的任務已完成時,創建將完成的任務。
public  static  Task<Task> WhenAny(IEnumerable<Task> tasks);
//任何提供的任務已完成時,創建將完成的任務。
public  static  Task<Task<TResult>> WhenAny<TResult>( params  Task<TResult>[] tasks);
//任何提供的任務已完成時,創建將完成的任務。
public  static  Task<Task<TResult>> WhenAny<TResult>(IEnumerable<Task<TResult>> tasks);

 使用:

(1)不帶參數

1
public  static  Task<Task> WhenAny( params  Task[] tasks);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private  static  void  fun14()
{
     Task t1 = Task.Factory.StartNew(() =>
     {
         Thread.Sleep(100);
         Console.WriteLine( "task1" );
     });
 
     Task t2 = Task.Factory.StartNew(() =>
     {
         Thread.Sleep(150);
         Console.WriteLine( "task2" );
     });
 
     Task.WhenAny(t1, t2).ContinueWith(t =>
     {
         Console.WriteLine( "WhenAny1" );
     });
 
     Console.WriteLine( "main" );
}

  運行結果:

(2)帶參數:

1
public  static  Task<Task<TResult>> WhenAny<TResult>( params  Task<TResult>[] tasks);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private  static  void  fun15()
         {
             Task< string > t1 = Task.Factory.StartNew(() =>
             {
                 Thread.Sleep(100);
                 Console.WriteLine( "task1" );
                 return  "response task1" ;
             });
 
             Task< string > t2 = Task.Factory.StartNew< string >(() =>
             {
                 Thread.Sleep(150);
                 Console.WriteLine( "task2" );
                 return  "response task2" ;
             });
 
             Task.WhenAny( new  Task< string >[] { t1, t2 }).ContinueWith(t =>
             {
                 t.Result.ContinueWith(tt =>
                 {
                     Console.WriteLine(tt.Result);
                 });
             });
 
             Console.WriteLine( "main" );
         } 

運行結果:

6.ContinueWith

相當於回調

6.1使用

(1)使用lambda表達式方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private  static  void  fun1()
{
     Console.WriteLine( "start" );
     Task< string > task1 =  new  Task< string >(() =>
     {
         Console.WriteLine( "task0" );
         return  "task1" ;
     });
     Task< string > task2 = task1.ContinueWith(t =>
     {
         Console.WriteLine(t.Result);
         return  "task2" ;
     });
     task1.Start();
     Console.WriteLine( "end" );
     Console.ReadKey();
}

2.使用函數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private  static  void  fun2()
        {
            Console.WriteLine( "start" );
            Task< string > task1 =  new  Task< string >(doTask1);
            Task< string > task2 = task1.ContinueWith(doTask2);
            task1.Start();
            Console.WriteLine( "end" );
            Console.ReadKey();
        }
 
        private  static  string  doTask1()
        {
            Console.WriteLine( "task0" );
            return  "task1" ;
        }
 
        private  static  string  doTask2(Task< string > t)
        {
            Console.WriteLine(t.Result);
            return  "task2" ;
        }

  運行結果:

 6.2 ContineWith和task可能不在同一線程上

例:

1
2
3
4
5
6
7
8
9
10
private  static  void  fun16()
        {
            Task.Factory.StartNew(() =>
             {
                 Console.WriteLine($ "task {Thread.CurrentThread.ManagedThreadId}" );
             }).ContinueWith(t =>
             {
                 Console.WriteLine($ "ContinueWith {Thread.CurrentThread.ManagedThreadId}" );
             });
        }

  運行結果:

 七.TaskFactory類

方法:

StartNew 創建並啟動任務
ContinueWhenAll 創建一個延續任務,該任務在一組指定的任務完成后開始
ContinueWhenAny 創建一個延續 System.Threading.Tasks.Task,它將在提供的組中的任何任務完成后馬上開始
FromAsync 創建一個 System.Threading.Tasks.Task`1,表示符合異步編程模型模式的成對的開始和結束方法

1.ContinueWhenAll

相當於回調

效果其實和WhenAll差不多,只不過ContineWhenAll采用了回調的方式

使用:帶返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private  static  void  fun17()
{
     Task< string > t1 = Task.Factory.StartNew< string >(() =>
     {
         Console.WriteLine( "task1" );
         return  "task1" ;
     });
 
     Task< string > t2 = Task.Factory.StartNew< string >(() =>
     {
         Console.WriteLine( "task2" );
         return  "task2" ;
     });
 
     Task.Factory.ContinueWhenAll( new  Task[] { t1, t2 }, t =>
     {
         string  s =  "ContinueWhenAll:" ;
         foreach  (Task< string > item  in  t)
         {
             s += item.Result;
         }
         Console.WriteLine(s);
     });
}

  運行結果:

 

 2.ContinueWhenAny

相當於回調

效果其實和WhenAny差不多,只不過ContineWhenAny采用了回調的方式

使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private  static  void  fun18()
{
     Task< string > t1 = Task.Factory.StartNew< string >(() =>
     {
         Thread.Sleep(10);
         Console.WriteLine( "task1" );
         return  "task1" ;
     });
 
     Task< string > t2 = Task.Factory.StartNew< string >(() =>
     {
         Console.WriteLine( "task2" );
         return  "task2" ;
     });
 
     Task.Factory.ContinueWhenAny( new  Task[] { t1, t2 }, t =>
     {
         Console.WriteLine($ "{(t as Task<string>).Result} 先執行完" );
     });
}

  執行結果:

 

 3.FromAsync

相當於異步委托的精簡寫法,其中ContinueWith相當於異步委托中的callback

使用:

public Task<TResult> FromAsync<TArg1, TResult>(Func<TArg1, AsyncCallback, object, IAsyncResult> beginMethod, Func<IAsyncResult, TResult> endMethod, TArg1 arg1, object state);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static  void  Main( string [] args)
{
     fun20();
 
     Console.WriteLine( "Main" );
     Console.ReadKey();
}
 
private  static  void  fun20()
{
     var  func =  new  Func< string string >(x =>
     {
         Thread.Sleep(1000);
         Console.WriteLine(x);
         return  "callback" ;
     });
 
     Task.Factory.FromAsync(func.BeginInvoke, func.EndInvoke,  "func" null ).ContinueWith(t =>
     {
         Console.WriteLine(t.Result);
     });
}

  運行結果:

 

參考:https://www.cnblogs.com/zhaoshujie/p/11082753.html
https://www.cnblogs.com/yaosj/p/10342883.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM