C#多線程的簡單理解

1、CLR線程池基礎
建立和銷燬線程是一個昂貴的操做,因此CLR管理了一個線程池(thread pool),能夠將線程池當作一個黑盒。
CLR初始化時,線程池中是沒有線程的。線程的初始化與其餘線程同樣,可是在完成任務之後,該線程不會自行銷燬,而是以掛起的狀態返回到線程池。直到應用程序再次向線程池發出請求時,線程池裏掛起的線程就會再度激活執行任務。
2、 ThreadPool的簡單使用
能夠直接將一個運算放到線程池的隊列中進行工做;
2.1 調用方式
向線程池的隊列中添加一個」工做項「以及可選的狀態數據,能夠經過下面的兩種方式來進行異步的操做:
public static bool QueueUserWorkItem(WaitCallback callBack);
public static bool QueueUserWorkItem(WaitCallback callBack, object state);
2.2 使用示例編程

{
        public static void Excute()
        {
            Console.WriteLine("Main Thread:開始隊列進入一個異步線程;");
            ThreadPool.QueueUserWorkItem(ComputeBoundOp, 5);           
            Console.WriteLine("Main Thread:作主線程的其餘任務任務;");
            //模擬其餘任務
            Thread.Sleep(10000);
            Console.WriteLine("Main Thread End;");
        }

        private static void ComputeBoundOp(object state)
        {
            Console.WriteLine("In ComputeBoundOp: state={0}",state);

            //模擬其餘任務
            Thread.Sleep(20000);
            Console.WriteLine("ComputeBoundOp Thread End;");
        }
    }

運行結果:c#

2.3 取消線程的操做
簡單的操做實例
public class CancellationDemo
    {
        /// <summary>
        /// 構造一個CancellationTokenSource後,從它的Token屬性中得到一個或多個CancellationToken實例,並傳給你的操做,使操做能夠取消
        /// </summary>
        public static void CancellationGo()
        {
            //構造一個CancellationTokenSource
            CancellationTokenSource cts =new CancellationTokenSource();

            //取消後的執行操做
            cts.Token.Register(() => Console.WriteLine("canceled 1"));
            cts.Token.Register(() => Console.WriteLine("canceled 2"));

            //放入線程池開始工做
            ThreadPool.QueueUserWorkItem(x => Count(cts.Token, 100));

            Console.WriteLine("press  to cancel the operation");
            Console.ReadLine();
            cts.Cancel();          
        }

        /// <summary>
        /// 計數
        /// </summary>
        /// <param name="token"></param>
        /// <param name="countTo"></param>
        private static void Count(CancellationToken token,Int32 countTo)
        {
            for (int i = 0; i < countTo; i++)
            {
                if (token.IsCancellationRequested)
                {
                    Console.WriteLine("count is cancelled");
                    break;
                }
                Console.WriteLine(i);
                Thread.Sleep(2000);
            }
            Console.WriteLine("count is done");
        }
    }

運行結果:
多線程

3、Task
很容易調用ThreadPool的QueueUserWorkItem方法發起一次異步的運算;可是,沒有內建的機制讓你知道操做在何時完成,也沒有機制在操做完成時得到返回值;爲了克服一些限制,引入了任務概念;
Task類是封裝的一個任務類,內部使用的是ThreadPool類,提供了內建機制,讓你知道何時異步完成以及如何獲取異步執行的結果,而且還能取消異步執行的任務。
3.1 使用task和ThreadPool來完成相同的任務:
ThreadPool.QueueUserWorkItem(ComputeBoundOp, 5);
new Task(ComputeBoundOp,5).Start();
Task.Run(() => ComputeBoundOp(5));
3.2 Task的簡單使用異步

public class TaskDemo
    {
        public void Excute()
        {
            long input = 10000000;
            //建立一個task
            Task<Int64> t1 = new Task<Int64>((x) => Sum((Int64) x),
            input);
            t1.Start();

            //顯示等待任務執行完畢
            t1.Wait();
            Console.WriteLine("task1 result is {0},主線程Id:{1}", t1.Result,Thread.CurrentThread.ManagedThreadId);


            //演示能夠取消的任務
            CancellationTokenSource cst=new CancellationTokenSource();           
            //建立一個task
            Task<Int64> t2 = new Task<Int64>(()=> Sum(cst.Token,input));
            t2.Start();
            Thread.Sleep(1000);
            cst.Cancel();
            Console.WriteLine("task2 result is {0}", t2.Result);
          
        }

        private static Int64 Sum(Int64 n)
        {
            Int64 sum = 0;
            for (;n>0; n--)
            {
                checked
                {
                    sum += n;
                }
            }
            Console.WriteLine("計算線程Id:{0}",Thread.CurrentThread.ManagedThreadId);
            return sum;
        }

        //建立可取消的任務
        private static Int64 Sum(CancellationToken ct, Int64 n)
        {
            Int64 sum = 0;
            for (; n > 0; n--)
            {
                //    ct.ThrowIfCancellationRequested();
                if (!ct.IsCancellationRequested)
                {
                    checked
                    {
                        sum += n;
                    }
                    Thread.Sleep(10);
                }             
            }
            return sum;
        }
    }

運行結果:

咱們能夠發現,程序啓動了一個新的線程來計算;
當咱們添加了取消操做時,運算結果不一樣;
4、async和await
在.NET 4.5中引入的Async和Await兩個新的關鍵字後,用戶能以一種簡潔直觀的方式實現異步編程。甚至都不須要改變代碼的邏輯結構,就能將原來的同步函數改造爲異步函數。async

在內部實現上,Async和Await這兩個關鍵字由編譯器轉換爲狀態機,經過System.Threading.Tasks中的並行類實現代碼的異步執行。異步編程

4.1 一個簡單的例子函數

public  class DownLoadUrlAsync
    {

        public void Excute()
        {
            Console.WriteLine("this is before Excute,threadId is {0}", Thread.CurrentThread.ManagedThreadId);
            var t = GetUrl().ContinueWith(task =>
            {
                Console.WriteLine("this is ContinueWith Excute,threadId is {0},result is {1}",
                    Thread.CurrentThread.ManagedThreadId, task.Result);
            });
            Console.WriteLine("this is Excute,threadId is {0},t IsCompleted is {1}", Thread.CurrentThread.ManagedThreadId,t.IsCompleted);
        }
        private static async Task<int> GetUrl()
        {
            var httpClient = new HttpClient();

            var url = "https://www.cnblogs.com/";
            Console.WriteLine("this is before GetUrl,threadId is {0}", Thread.CurrentThread.ManagedThreadId);
            var res = await httpClient.GetByteArrayAsync(url);
            Console.WriteLine("this is GetUrl,threadId is {0}", Thread.CurrentThread.ManagedThreadId);
            return res.Length;
        }
    }

運行結果:
this

經過結果能夠發現,程序啓用了新的線程來完成咱們的任務;url

5、小結
一、C#可使用多種語法實現多線程 Thread,ThreadPool,Task,async await;
二、多線程能夠同時完成多個任務;可使程序的響應速度更快;可讓佔用大量處理時間的任務或當前沒有進行處理的任務按期將處理時間讓給別的任務;線程

相關文章
相關標籤/搜索