多線程編程之計算限制型非同步作業

來源:互聯網
上載者:User

1. CLR線程池簡介

1.1  CLR為什麼支援線程池

1.2 線程池ThreadPool管理線程 

2.  線程執行內容

2.1 線程執行內容簡介

2.2  一個簡單樣本

3. 線程池常見應用情景樣本

3.1 將一個線程添加至線程池中(向線程傳遞參數) 

3.2 協作式取消 

4. Task簡介

5. Task編程基礎 

6. 定時器Timer 

<1>. CLR線程池簡介

1.1 CLR為什麼支援線程池?

上一篇中講到如果在一個應用程式中啟動了多個線程的話,顯然是會影響到程式的運行效率,一種很直觀的想法是:如果一個線程完成了任務,我們並不在記憶體中銷毀這個線程的執行個體,而是將這個線程進入空閑狀態,如果又來了一個請求,可以不用另外建立新線程,這樣顯然能夠帶來效能上的提升,在.net中,CLR為我們實現了線程池,它可以看作是線程的一個集合。每個CLR都存在一個全域的線程池,供所有的AppDomain共用。

1.2 線程池ThreadPool管理線程

 System.Threading.ThreadPool中提供了查詢和設定線程池性質的的靜態方法。

public static bool SetMaxThreads(int workerThreads, int completionPortThreads); // 設定線程池中線程數量的最大值,但是永遠不要折磨做,容易發生死結等情況

public static bool SetMinThreads(int workerThreads, int completionPortThreads); // 設定線程池中線程數量的最小值

public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads);  // 返回線程池支援的最大線程數和當前活動的線程的差值

<2>. 線程執行的上下文及其流轉

2.1 線程執行內容簡介 

線程的執行內容包含安全設定,宿主設定,線程上下文資料(System.Runtime.Remoting.Messaging.CallContext的LogicalSetData方法)等資訊。當一個線程使用另外的線程執行任務時,前者的線程執行內容將流轉至後者的線程執行內容中。在c#中類型System.Threading.ExecutionContext來控制線程執行內容的流轉:

public static AsyncFlowControl SuppressFlow(); // 阻止( Suppress本意是抑制)線程執行內容的流轉

public static bool IsFlowSuppressed();  // 判斷當先線程執行內容是否被阻止

2.2 一個簡單樣本

 static void Main(string[] args)        {            // 見一些資料儲存在Main線程的執行內容中            CallContext.LogicalSetData("myname", "xuqiang");            // 線程執行內容流轉,所以這裡是能夠得到myname的值的            ThreadPool.QueueUserWorkItem            (                state =>                {                    // 將輸出xuqiang                    Console.WriteLine("MyName :" + CallContext.LogicalGetData("myname"));                  }            );            // 現在阻止線程內容相關的流動            ExecutionContext.SuppressFlow();            // 下面的線程將無法訪問Main線程的上下文            ThreadPool.QueueUserWorkItem            (                state =>                {                    // 不會輸出xuqiang                    Console.WriteLine("MyName :" + CallContext.LogicalGetData("myname"));                }            );            // 回複上下文流轉            ExecutionContext.RestoreFlow();            // 等待線程執行完畢            Thread.Sleep(1000);            Console.WriteLine("Press any key...");            Console.ReadKey();

        } 

通過上面的建立樣本,我們能夠明確,

1. 如何向線程池中添加背景工作執行緒: 

通過調用QueueUserWorkItem,該靜態方法存在兩個重載方法:

public static bool QueueUserWorkItem(WaitCallback callBack); // 不需要傳遞參數

public static bool QueueUserWorkItem(WaitCallback callBack, object state); // 需要向線程傳遞參數 

2. 通過函數SuppressFlown能夠阻止線程內容相關的流轉,通過RestoreFlow能夠恢複線程上下文

3. 通過Thread.Sleep函數能夠阻塞調用Thread.Sleep的線程(這裡是指Main線程) 

<3>. 線程池常見應用情境樣本

3.1 將一個線程添加至線程池中(向線程中傳遞參數)

 static void Main(string[] args)        {            Console.WriteLine("Main thread: queuing an asychronous operation");            // 向線程池中添加一個背景工作執行緒,同時傳遞參數5            ThreadPool.QueueUserWorkItem(ComputeOp, 5);            Console.WriteLine("Main thread: Doing other work here..");                        // 將main線程休眠一段時間,以保證computeOp執行完成            Thread.Sleep(10000);    // 10秒            Console.WriteLine("Hit <Enter> to end this program");            Console.ReadKey();        }        private static void ComputeOp(object state)        {            Console.WriteLine("CompiteOp: state is " + state);                        // 類比工作1s            Thread.Sleep(1000);            // 這個線程返回到線程池中,將等待另外的任務

        } 

3.2 協作式取消

之所以稱之為“協作式取消”,是講一個線程T如果想要獲得被取消的能力的話,那麼T需要接受一個類型為CancellationToken參數(該類型存在於.net 4.0中,3.5中不存在),在T中通過查詢CancellationToken的IsCancellationRequested屬性來判斷是否已經被取消。下面結合這個情境來分析這個過程中使用到的類CancellationTokenSource和CancellationToken:

public sealed class CancellationTokenSource : IDisposable

{

public CancellationTokenSource(); // 預設建構函式

public bool IsCancellationRequested { get; } // 查詢線程是否已經被取消

public CancellationToken Token { get; } // 構造好了CancellationTokenSource通過該方法得到CancellationToken執行個體,然後傳遞給需要實現協作式取消的線程T

public void Cancel(); // 通過該方法取消T線程

// 取消線程T的重載版本,可以通過CancellationTokenSource的CancellationToken屬性的Register註冊委託,在Cancel方法調用後執行。如

// 果throwOnFirstException為true的話,那麼在註冊的方法中的任何一個方法拋出未處理異常,Cancel將拋出該異常 ,同時阻止其他回調方法

//  的調用,如果throwOnFirstException為false的話,如果在任何的回調方法中拋出異常不會影響到其他回調方法的調用,未處理的異常將添加

// 至AggregateException中

public void Cancel(bool throwOnFirstException); 

public void Dispose(); // 實現IDisposable介面

// 串連另外一組 CancellationToken來建立CancellationTokenSource樣本,例如如果通過var cts  = CreateLinkedTokenSource(token1, token2)

// 那麼如果token1或者token2取消,cts將被取消

public static CancellationTokenSource CreateLinkedTokenSource(params CancellationToken[] tokens); 

// 重載版本 

public static CancellationTokenSource CreateLinkedTokenSource(CancellationToken token1, CancellationToken token2);

 public struct CancellationToken

{

public bool IsCancellationRequested { get; }// 得到是否已經存在對於該CancellationToken的取消操作

public static CancellationToken None { get; } // 如果一個背景工作處理序不想被取消的話,可以使用該靜態欄位

// 下面的幾個方法將向 CancellationToken中註冊委託時間,如果CancellationToken被取消,將調用這些方法

public CancellationTokenRegistration Register(Action callback); 

public CancellationTokenRegistration Register(Action<object> callback, object state); 

// 該方法主要解決在winform,wpf等中,背景工作執行緒如何更新ui線程 

public CancellationTokenRegistration Register(Action callback, bool useSynchronizationContext); 

.... 

這個樣本示範了如何使用CancellationTokenSource,並向其中註冊回調方法,如何管理異常等特性:

static void Main(string[] args)        {            Go();        }        public static void Go()        {            // 產生CancellationTokenSource對象            CancellationTokenSource cts = new CancellationTokenSource();            // 向cts中添加回調事件,但token調用cancel方法時,將調用這個方法            cts.Token.Register( WhenCancel1 );            // 將拋出異常            cts.Token.Register(WhenCancel2 );            cts.Token.Register(WhenCancel3);            // 向線程隊列中添加一個背景工作執行緒            ThreadPool.QueueUserWorkItem            (                 o => Count(cts.Token, 1000)            );            Console.WriteLine("Press enter to cancel the operation .");            Console.ReadLine();            try            {                // 如果為true的話,將僅僅調用WhenCancel3方法                // 如果為false將調用這三個回調方法                 cts.Cancel(true);            }            catch (AggregateException ex)            {            Console.WriteLine(ex.InnerException.ToString());            }                   }        private static void WhenCancel1()        {            Console.WriteLine("When cancel 1 delegate called.");        }                 private static void WhenCancel2()        {           // Console.WriteLine("When cancel 3 delegate called.");             throw new Exception();        }         private static void WhenCancel3()        {            Console.WriteLine("When cancel 3 delegate called.");        }        private static void Count(CancellationToken token, Int32 countTo)        {            for (Int32 count = 0; count < countTo; ++count )            {                // 如果被取消                 if(token.IsCancellationRequested)                {                    Console.WriteLine("The operation is cancel.");                    break;                }                // 列印                Console.WriteLine(count);                // 休眠該線程一段時間                Thread.Sleep(200);              }            Console.WriteLine("Count is done.");

        } 

 下面的樣本將示範如何?一個連結的CancellationTokenSource

public static void Go()        {            // 產生CancellationTokenSource對象            CancellationTokenSource cts1 = new CancellationTokenSource();            cts1.Token.Register(() => Console.WriteLine("cts1 is cancel"));            CancellationTokenSource cts2 = new CancellationTokenSource();            cts2.Token.Register(() => Console.WriteLine("cts2 is cancel"));            // 建立連結            CancellationTokenSource linkedCts =                 CancellationTokenSource.CreateLinkedTokenSource(cts1.Token, cts2.Token);                // 如果取消cts1或者是cts2中的一個,那麼linkedCts將被取消            cts1.Cancel();            Console.WriteLine("cts1 cancel = {0}, cts2 cancel = {1}",                cts1.IsCancellationRequested,                cts2.IsCancellationRequested);            Console.WriteLine("Press enter to cancel the operation .");            Console.ReadLine();

        }     

 下一篇繼續... 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.