C#中的併發編程知識

同步編程是對於單線程來講的,就像咱們編寫的控制檯程序,以main方法爲入口,順序執行咱們編寫的代碼。數據庫

異步編程是對於多線程來講的,經過建立不一樣線程來實現多個任務的並行執行。編程

線程

多線程的意義在於一個應用程序中,有多個執行部分能夠同時執行;對於比較耗時的操做(例如io,數據庫操做),或者等待響應(如WCF通訊)的操做,能夠單獨開啓後臺線程來執行,這樣主線程就不會阻塞,能夠繼續往下執行;等到後臺線程執行完畢,再通知主線程,而後作出對應操做!數組

什麼是主線程

每個Windows進程都剛好包含一個用做程序入口點的主線程。進程的入口點建立的第一個線程被稱爲主線程。.Net執行程序(控制檯、Windows Form、Wpf等)使用Main()方法做爲程序入口點。當調用該方法時,主線程被建立。多線程

什麼是工做者線程

由主線程建立的線程,能夠稱爲工做者線程,用來去執行某項具體的任務。併發

什麼是前臺線程

默認狀況下,使用Thread.Start()方法建立的線程都是前臺線程。前臺線程能阻止應用程序的終結,只有全部的前臺線程執行完畢,CLR才能關閉應用程序(即卸載承載的應用程序域)。前臺線程也屬於工做者線程。asp.net

什麼是後臺線程

後臺線程不會影響應用程序的終結,當全部前臺線程執行完畢後,後臺線程不管是否執行完畢,都會被終結。通常後臺線程用來作些可有可無的任務(好比郵箱每隔一段時間就去檢查下郵件,天氣應用每隔一段時間去更新天氣)。後臺線程也屬於工做者線程。異步

在C#中開啓新線程比較簡單async

static void Main(string[] args)
        {
            Console.WriteLine("主線程開始!");
            //建立前臺工做線程
            Thread t1 = new Thread(Task1);
            t1.Start();
            //建立後臺工做線程
            Thread t2= new Thread(new ParameterizedThreadStart(Task2));
            t2.IsBackground = true;//設置爲後臺線程
            t2.Start("傳參");
        }
        private static void Task1()
        {
            Thread.Sleep(1000);//模擬耗時操做,睡眠1s
            Console.WriteLine("前臺線程被調用!");
        }
        private static void Task2(object data)
        {
            Thread.Sleep(2000);//模擬耗時操做,睡眠2s
            Console.WriteLine("後臺線程被調用!" + data);
        }

線程池

試想一下,若是有大量的任務須要處理,例如網站後臺對於HTTP請求的處理,那是否是要對每個請求建立一個後臺線程呢?顯然不合適,這會佔用大量內存,並且頻繁地建立的過程也會嚴重影響速度,那怎麼辦呢?線程池就是爲了解決這一問題,把建立的線程存起來,造成一個線程池(裏面有多個線程),當要處理任務時,若線程池中有空閒線程(前一個任務執行完成後,線程不會被回收,會被設置爲空閒狀態),則直接調用線程池中的線程執行(例asp.net處理機制中的Application對象)ide

線程池是爲忽然大量爆發的線程設計的,經過有限的幾個固定線程爲大量的操做服務,減小了建立和銷燬線程所需的時間,從而提升效率,這也是線程池的主要好處。異步編程

ThreadPool適用於併發運行若干個任務且運行時間不長且互不干擾的場景。

還有一點須要注意,經過線程池建立的任務是後臺任務。

for (int i = 0; i < 100; i++)
        {
            //將方法排入隊列以便執行,此方法在線程池中線程變的能夠時執行
            ThreadPool.QueueUserWorkItem(m =>
            {
                Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString());
            });
        }
        Console.Read();

        //雖然執行了100次,但並無建立100個線程。
        //若是去掉最後一句Console.Read(),會發現程序僅輸出【主線程開始!】就直接退出,從而肯定ThreadPool建立的線程都是後臺線程。

信號量(Semaphore)

信號量(Semaphore),有時被稱爲信號燈,是在多線程環境下使用的一種設施, 它負責協調各個線程, 以保證它們可以正確、合理的使用公共資源。

以一個停車場是運做爲例。爲了簡單起見,假設停車場只有三個車位,一開始三個車位都是空的。這是若是同時來了五輛車,看門人容許其中三輛不受阻礙的進入,而後放下車攔,剩下的車則必須在入口等待,此後來的車也都不得不在入口處等待。這時,有一輛車離開停車場,看門人得知後,打開車攔,放入一輛,若是又離開兩輛,則又能夠放入兩輛,如此往復。   在這個停車場系統中,車位是公共資源,每輛車比如一個線程,看門人起的就是信號量的做用。   更進一步,信號量的特性以下:信號量是一個非負整數(車位數),全部經過它的線程(車輛)都會將該整數減一(經過它固然是爲了使用資源),當該整數值爲零時,全部試圖經過它的線程都將處於等待狀態。在信號量上咱們定義兩種操做: Wait(等待) 和 Release(釋放)。 當一個線程調用Wait等待)操做時,它要麼經過而後將信號量減一,要麼一自等下去,直到信號量大於一或超時。Release(釋放)其實是在信號量上執行加操做,對應於車輛離開停車場,該操做之因此叫作「釋放」是應爲加操做其實是釋放了由信號量守護的資源。

相似互斥鎖,但它能夠容許多個線程同時訪問一個共享資源。經過使用一個計數器來控制對共享資源的訪問,若是計數器大於0,就容許訪問,若是等於0,就拒絕訪問。計數器累計的是「許可證」的數目,爲了訪問某個資源。線程必須從信號量獲取一個許可證。

Semaphore負責協調線程,能夠限制對某一資源訪問的線程數量

using  System.Threading

        static SemaphoreSlim semLim = new SemaphoreSlim(3); //3表示最多隻能有三個線程同時訪問
        static void Main(string[] args)
        {
            for (int i = 0; i < 10; i++)
            {
                new Thread(SemaphoreTest).Start();
            }
            Console.Read();
        }
        static void SemaphoreTest()
        {
            semLim.Wait();
            Console.WriteLine("線程" + Thread.CurrentThread.ManagedThreadId.ToString() + "開始執行");
            Thread.Sleep(2000);
            Console.WriteLine("線程" + Thread.CurrentThread.ManagedThreadId.ToString() + "執行完畢");
            semLim.Release();
        }

能夠看到,剛開始只有三個線程在執行,當一個線程執行完畢並釋放以後,纔會有新的線程來執行方法!除了SemaphoreSlim類,還可使用Semaphore類,感受更加靈活

Semaphore

Public Semaphore(int initialCount,int maximumCount)

initialCount指信號量許可證的初始值,maximumCount爲最大值.獲取許可證使用WaitOne(),不須要時釋放使用 public int Release()或者public int Release(int releaseCount)

public class MyThread
        {
            public Thread thrd;
            //建立一個可受權2個許可證的信號量,且初始值爲2
            static Semaphore sem = new Semaphore(2, 2);
            public MyThread(string name)
            {
                thrd = new Thread(this.run);
                thrd.Name = name;
                thrd.Start();
            }
            void run()
            {
                Console.WriteLine(thrd.Name + "正在等待一個許可證……");
                //申請一個許可證
                sem.WaitOne();
                Console.WriteLine(thrd.Name + "申請到許可證……");
                for (int i = 0; i < 4; i++)
                {
                    Console.WriteLine(thrd.Name + ": " + i);
                    Thread.Sleep(1000);
                }
                Console.WriteLine(thrd.Name + " 釋放許可證……");
                //釋放
                sem.Release();
            }
        }

        class Program
        {
            public static void Main()
            {
                mythread mythrd1 = new mythread("Thrd #1");
                mythread mythrd2 = new mythread("Thrd #2");
                mythread mythrd3 = new mythread("Thrd #3");
                mythread mythrd4 = new mythread("Thrd #4");
                mythrd1.thrd.Join();
                mythrd2.thrd.Join();
                mythrd3.thrd.Join();
                mythrd4.thrd.Join();
            }
        }

Task

Task是.NET4.0加入的,跟線程池ThreadPool的功能相似,用Task開啓新任務時,會從線程池中調用線程,而Thread每次實例化都會建立一個新的線程,簡化了咱們進行異步編程的方式,而不用直接與線程和線程池打交道。用Task類能夠輕鬆地在次線程中調用方法。

System.Threading.Tasks中的類型被稱爲任務並行庫(TPL)。TPL使用CLR線程池(說明使用TPL建立的線程都是後臺線程)自動將應用程序的工做動態分配到可用的CPU中。

Console.WriteLine("主線程啓動");
        //Task.Run啓動一個線程
        //Task啓動的是後臺線程,要在主線程中等待後臺線程執行完畢,能夠調用Wait方法
        //Task task = Task.Factory.StartNew(() => { Thread.Sleep(1500); Console.WriteLine("task啓動"); });
        Task task = Task.Run(() => { 
            Thread.Sleep(1500);
            Console.WriteLine("task啓動");
        });
        Thread.Sleep(300);
        task.Wait();
        Console.WriteLine("主線程結束");

開啓新任務的方法:Task.Run()或者Task.Factory.StartNew(),開啓的是後臺線程。要在主線程中等待後臺線程執行完畢,可使用Wait方法(會以同步的方式來執行)。不用Wait則會以異步的方式來執行。

比較一下Task和Thread:

static void Main(string[] args)
        {
            for (int i = 0; i < 5; i++)
            {
                new Thread(Run1).Start();
            }
            for (int i = 0; i < 5; i++)
            {
                Task.Run(() => { Run2(); });
            }
        }
        static void Run1()
        {
            Console.WriteLine("Thread Id =" + Thread.CurrentThread.ManagedThreadId);
            /*
            Thread Id=10
            Thread Id=11
            Thread Id=12
            Thread Id=13
            Thread Id=14            
            */
        }
        static void Run2()
        {
            Console.WriteLine("Task調用的Thread Id =" + Thread.CurrentThread.ManagedThreadId);

            /*
            Task調用的Thread Id =15
            Task調用的Thread Id =6
            Task調用的Thread Id =6
            Task調用的Thread Id =17
            Task調用的Thread Id =15
            */
        }

能夠看出來,直接用Thread會開啓5個線程,用Task(用了線程池)開啓了3個!

Task<TResult>

Task<TResult>就是有返回值的Task,TResult就是返回值類型。

Console.WriteLine("主線程開始");
        //返回值類型爲string
        Task<string> task = Task<string>.Run(() => {
            Thread.Sleep(2000); 
            return Thread.CurrentThread.ManagedThreadId.ToString(); 
        });
        //會等到task執行完畢纔會輸出;
        Console.WriteLine(task.Result);
        Console.WriteLine("主線程結束");

        /*
        主線程開始
        10
        主線程結束
        */

經過task.Result能夠取到返回值,若取值的時候,後臺線程還沒執行完,則會等待其執行完畢!Task任務能夠經過CancellationTokenSource類來取消,感受用得很少

Task線程取消

Task 提供了線程取消的操做,使用起來也是很是的簡單。它本質上是靠異常來打斷線程的執行,而且把Task的狀態置爲Cancel狀態

要實現線程的取消須要一下步驟。

  • 首先要在線程中加入取消檢查點(ThrowIfCancellationRequested),這裏是在工做函數的起始處和while循環的運行中。
  • 在Main函數中定義CancellationTokenSource對象並把它做爲參數傳遞給工做Task。
  • 在運行時調用CancellationTokenSource.Cancel(),觸發線程的取消。
  • 在Main函數中捕獲取消異常(OperationCanceledException),線程終止,取消成功。
public void run_with_cancel(System.Threading.CancellationToken ct)
        {           
            System.Console.WriteLine("ThreadWork1 run { ");
            ct.ThrowIfCancellationRequested();
            for (int i = 0; i < 10; i++)
            {
                System.Console.WriteLine("ThreadWork1 : " + i);
                System.Threading.Thread.Sleep(200);
                ct.ThrowIfCancellationRequested();
            }
            System.Console.WriteLine("ThreadWork1 run } ");           
        }
        
        public void run_with_cancel(System.Threading.CancellationToken ct)
        {            
            ct.ThrowIfCancellationRequested();
            System.Console.WriteLine("ThreadWork2 run { ");
            for (int i = 0; i < 10; i++)
            {
                System.Console.WriteLine("ThreadWork2 : " + i * i);
                System.Threading.Thread.Sleep(300);
                ct.ThrowIfCancellationRequested();
            }
            System.Console.WriteLine("ThreadWork2 run } ");            
        }
static void StartT1(System.Threading.CancellationToken ct)
        {
            ThreadWork1 work1 = new ThreadWork1();
            work1.run_with_cancel(ct);
        }

        static void StartT2(System.Threading.CancellationToken ct)
        {
            ThreadWork2 work2 = new ThreadWork2();
            work2.run_with_cancel(ct);
        }
System.Threading.CancellationTokenSource cts =new System.Threading.CancellationTokenSource();  
        System.Threading.CancellationToken ct = cts.Token;
        Task t1 = new Task(() => StartT1(ct)); //傳入Token
        Task t2 = new Task(() => StartT2(ct)); //傳入Token
        t1.Start();
        t2.Start();
        System.Threading.Thread.Sleep(2000);
        cts.Cancel(); //觸發取消
        try{
                Console.WriteLine("Main wait t1 t2 end {");
                if (!Task.WaitAll(new Task[] { t1, t2 }, 5000))
                {
                    Console.WriteLine("Worker1 and Worker2 NOT complete within 5 seconds");
                    Console.WriteLine("Worker1 Status: " + t1.Status);
                    Console.WriteLine("Worker2 Status: " + t2.Status);
                }
                else
                {
                    Console.WriteLine("Worker1 and Worker2 complete within 5 seconds");
                }
            }
            catch (AggregateException agg_ex)
            {
                foreach (Exception ex in agg_ex.InnerExceptions)
                {
                    Console.WriteLine("Agg Exceptions: " + ex.ToString());
                    Console.WriteLine("");
                }
            }

async/await

async/await是C#4.5中推出的,async關鍵字用來指定某個方法、Lambda表達式或匿名方法自動以異步的方式來調用,先上用法

static void Main(string[] args)
        {
            Console.WriteLine("-------主線程啓動-------");
            Task<int> task = GetStrLengthAsync();
            Console.WriteLine("主線程繼續執行");
            Console.WriteLine("Task返回的值" + task.Result);
            Console.WriteLine("-------主線程結束-------");
        }
        
        static async Task<int> GetStrLengthAsync()
        {
            Console.WriteLine("GetStrLengthAsync方法開始執行");
            //此處返回的<string>中的字符串類型,而不是Task<string>
            string str = await GetString();
            Console.WriteLine("GetStrLengthAsync方法執行結束");
            return str.Length;
        }
        
        static Task<string> GetString()
        {
           Console.WriteLine("GetString方法開始執行")
            return Task<string>.Run(() =>
            {
                Thread.Sleep(2000);
                return "GetString的返回值";
            });
        }

        /*
        -------主線程啓動-------
        GetStrLengthAsync方法開始執行
        GetString方法開始執行
        主線程繼續執行
        GetStrLengthAsync方法執行結束
        Task返回的值13
        -------主線程結束-------
        */

async用來修飾方法,代表這個方法是異步的,聲明的方法的返回類型必須爲:void,Task或Task<TResult>。方法的執行結果或者任何異常都將直接反映在返回類型中

await必須用來修飾Task或Task<TResult>,並且只能出如今已經用async關鍵字修飾的異步方法中。一般狀況下,async/await成對出現纔有意義

能夠看出來,main函數調用GetStrLengthAsync方法後,在await以前,都是同步執行的,直到遇到await關鍵字,main函數才返回繼續執行

在遇到await關鍵字後,沒有繼續執行GetStrLengthAsync方法後面的操做,也沒有立刻反回到main函數中,而是執行了GetString的第一行,以此能夠判斷await這裏並無開啓新的線程去執行GetString方法,而是以同步的方式讓GetString方法執行,等到執行到GetString方法中的Task<string>.Run()的時候才由Task開啓了後臺線程!

被async標記的方法,意味着能夠在方法內部使用await,這樣該方法將會在一個await point(等待點)處被掛起,而且在等待的實例完成後該方法被異步喚醒。【注意:await point(等待點)處被掛起,並非說在代碼中使用await SomeMethodAsync()處就掛起,而是在進入SomeMethodAsync()真正執行異步任務時被掛起,切記

不是被async標記的方法,就會被異步執行,剛開始都是同步開始執行。換句話說,方法被async標記不會影響方法是同步仍是異步的方式完成運行。事實上,async使得方法能被分解成幾個部分,一部分同步運行,一些部分能夠異步的運行(而這些部分正是使用await顯示編碼的部分),從而使得該方法能夠異步的完成。調用async標記的方法,剛開始是同步執行的,只有當執行到await標記的方法中的異步任務時,纔會掛起。

那麼await的做用是什麼呢?

await關鍵字告訴編譯器在async標記的方法中插入一個可能的掛起/喚醒點。 邏輯上,這意味着當你寫await someMethod();時,編譯器將生成代碼來檢查someMethod()表明的操做是否已經完成。若是已經完成,則從await標記的喚醒點處繼續開始同步執行;若是沒有完成,將爲等待的someMethod()生成一個continue委託,當someMethod()表明的操做完成的時候調用continue委託。這個continue委託將控制權從新返回到async方法對應的await喚醒點處。返回到await喚醒點處後,無論等待的someMethod()是否已經經完成,任何結果均可從Task中提取,或者若是someMethod()操做失敗,發生的任何異常隨Task一塊兒返回或返回給SynchronizationContext。

能夠從字面上理解,上面提到task.wait可讓主線程等待後臺線程執行完畢,await和wait相似,一樣是等待,等待Task<string>.Run()開始的後臺線程執行完畢,不一樣的是await不會阻塞主線程,只會讓GetStrLengthAsync方法暫停執行。

IAsyncResult

IAsyncResult自.NET1.1起就有了,包含可異步操做的方法的類須要實現它,Task類就實現了該接口

在不借助於Task的狀況下怎麼實現異步呢?

class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("主程序開始--------------------");
                int threadId;
                AsyncDemo ad = new AsyncDemo();
                AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);
        
                IAsyncResult result = caller.BeginInvoke(3000,out threadId, null, null);
                Thread.Sleep(0);
                Console.WriteLine("主線程線程 {0} 正在運行.",Thread.CurrentThread.ManagedThreadId)
                //會阻塞線程,直到後臺線程執行完畢以後,纔會往下執行
                result.AsyncWaitHandle.WaitOne();
                Console.WriteLine("主程序在作一些事情!!!");
                //獲取異步執行的結果
                string returnValue = caller.EndInvoke(out threadId, result);
                //釋放資源
                result.AsyncWaitHandle.Close();
                Console.WriteLine("主程序結束--------------------");
                Console.Read();
            }
        }
        public class AsyncDemo
        {
            //供後臺線程執行的方法
            public string TestMethod(int callDuration, out int threadId)
            {
                Console.WriteLine("測試方法開始執行.");
                Thread.Sleep(callDuration);
                threadId = Thread.CurrentThread.ManagedThreadId;
                return String.Format("測試方法執行的時間 {0}.", callDuration.ToString());
            }
        }
        public delegate string AsyncMethodCaller(int callDuration, out int threadId);

            /*
            主程序開始--------------------
            主線程線程9正在運行.
            測試方法開始執行.
            測試方法執行完畢.
            主程序在作一些事情!!!
            主程序結束--------------------
            */

和Task的用法差別不是很大!result.AsyncWaitHandle.WaitOne()就相似Task的Wait。

Parallel(數據並行)

數據並行是指使用Parallel.For()或Parallel.ForEach()方法以並行方式對數組或集合中的數據進行迭代。

Stopwatch watch1 = new Stopwatch();
        watch1.Start();
        for (int i = 1; i <= 10; i++)
        {
            Console.Write(i + ",");
            Thread.Sleep(1000);
        }
        watch1.Stop();
        Console.WriteLine(watch1.Elapsed);
        
        Stopwatch watch2 = new Stopwatch();
        watch2.Start();
        
        //會調用線程池中的線程
        Parallel.For(1, 11, i =>
        {
            Console.WriteLine(i + ",線程ID:" + Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(1000);
        });
        watch2.Stop();
        Console.WriteLine(watch2.Elapsed);
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 6, 7, 8, 9 };
        Parallel.ForEach<int>(list, n =>
        {
            Console.WriteLine(n);
            Thread.Sleep(1000);
        });
Action[] actions = new Action[] { 
           new Action(()=>{
               Console.WriteLine("方法1");
           }),
            new Action(()=>{
               Console.WriteLine("方法2");
           })
        };
        Parallel.Invoke(actions);

PLINQ(並行LINQ查詢)

爲並行運行而設計的LINQ查詢爲PLINQ。System.Linq命名空間的ParallelEnumerable中包含了一些擴展方法來支持PINQ查詢。

int[] modThreeIsZero = (from num in source.AsParallel()
                        where num % 3 == 0
                        orderby num descending
                        select num).ToArray();

異步的回調

爲了簡潔(偷懶),文中全部Task<TResult>的返回值都是直接用task.result獲取,這樣若是後臺任務沒有執行完畢的話,主線程會等待其執行完畢。這樣的話就和同步同樣了,通常狀況下不會這麼用。簡單演示一下Task回調函數的使用:

Console.WriteLine("主線程開始");
        Task<string> task = Task<string>.Run(() => {
            Thread.Sleep(2000); 
            return Thread.CurrentThread.ManagedThreadId.ToString(); 
        });
        //會等到任務執行完以後執行
        task.GetAwaiter().OnCompleted(() =>
        {
            Console.WriteLine(task.Result);
        });
        Console.WriteLine("主線程結束");
        Console.Read();

            /*
            主線程開始
            主線程結束
            10
            */

OnCompleted中的代碼會在任務執行完成以後執行!另外task.ContinueWith()也是一個重要的方法:

Console.WriteLine("主線程開始");
        Task<string> task = Task<string>.Run(() => {
            Thread.Sleep(2000); 
            return Thread.CurrentThread.ManagedThreadId.ToString(); 
        });
        
        task.GetAwaiter().OnCompleted(() =>
        {
            Console.WriteLine(task.Result);
        });
        task.ContinueWith(m=>{Console.WriteLine("第一個任務結束啦!我是第二個任務");});
        Console.WriteLine("主線程結束");
        Console.Read();

            /*
            主線程開始
            主線程結束
            10
            第一個任務結束啦!我是第二個任務
            */

ContinueWith()方法可讓該後臺線程繼續執行新的任務,控制Task簡單的任務執行順序的方式。

用ContinueWith和Wait函數組合使用即可以控制任務的運行順序。

class ThreadWork1
         {
             public ThreadWork1()
             { }

             public List<string> run()
             {
                 List<string> RetList = new List<string>();
                 System.Console.WriteLine("ThreadWork1 run { ");
                 System.Console.WriteLine("ThreadWork1 running ... ... ");
                 for (int i = 0; i < 100; i++)
                 {
                     RetList.Add("ThreadWork1 : " + i);
                     //System.Console.WriteLine("ThreadWork1 : " + i);
                 }
                 System.Console.WriteLine("ThreadWork1 run } ");

                 return RetList;
             }
         }

         class ThreadWork2
         {
             public ThreadWork2()
             { }

             public List<string> run()
             {
                 List<string> RetList = new List<string>();

                 System.Console.WriteLine("ThreadWork2 run { ");
                 System.Console.WriteLine("ThreadWork2 running ... ... ");
                 for (int i = 0; i < 100; i++)
                 {
                     RetList.Add("ThreadWork2 : " + i);
                     //System.Console.WriteLine("ThreadWork2 : " + i * i);
                 }
                 System.Console.WriteLine("ThreadWork2 run } ");
                 return RetList;
             }
         }

         class Program
         {
             static void StartT0()
             {
                 System.Console.WriteLine("Hello I am T0 Task, sleep 3 seconds. when I am ready others GO!");  
                 for (int i = 0; i < 3; i++)
                 {
                     Console.WriteLine("StartT0 sleeping  ... ... " + i);
                     System.Threading.Thread.Sleep(1000);
                 }
             }

             static List<string> StartT1()
             {
                 ThreadWork1 work1 = new ThreadWork1();
                 return work1.run();
             }

             static List<string> StartT2()
             {
                 ThreadWork2 work2 = new ThreadWork2();
                 return work2.run();
             }
             static void Main(string[] args)
             {
                 Console.WriteLine("Sample 3-4 Main {");
                 // The sequence of the task is:
                 // T0 (Wait 3s) --> |
                 //                  | --> T1 (Cacluate) |
                 //                  | --> T2 (Cacluate) |
                 //                                      |  --> T3 (Print)

                 var t0 = Task.Factory.StartNew(() => StartT0());
                 var t1 = t0.ContinueWith((t) => StartT1());
                 var t2 = t0.ContinueWith((t) => StartT2());

                 Console.WriteLine("Main wait t1 t2 end {");
                 Task.WaitAll(t1, t2);
                 Console.WriteLine("Main wait t1 t2 end }");

                 var t3 = Task.Factory.StartNew(() =>
                 {
                     Console.WriteLine("============= T1 Result =============");
                     for (int i = 0; i < t1.Result.Count; i++)
                     {
                         Console.WriteLine(t1.Result[i]);
                     }
                     Console.WriteLine("============= ========= =============\n\n");

                     Console.WriteLine("============= T2 Result =============");
                     for (int i = 0; i < t2.Result.Count; i++)
                     {
                         Console.WriteLine(t2.Result[i]);
                     }
                     Console.WriteLine("============= ========= =============\n\n");
                 }, TaskCreationOptions.LongRunning);

                 Console.WriteLine("Sample 3-4 Main }");

                 Console.ReadKey();
             }
         }
相關文章
相關標籤/搜索