<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # [C# 線程處理系列]專題二:線程池中的工作者線程 **目錄:** 一、上節補充 二、CLR線程池基礎 三、通過線程池的工作者線程實現異步 四、使用委托實現異步 五、任務 **一、上節補充** 對于Thread類還有幾個常用方法需要說明的。 **1.1 Suspend和Resume方法** 這兩個方法在.net Framework 1.0的時候就支持的方法,他們分別可以掛起線程和恢復掛起的線程。但在.net Framework 2.0以后的版本中這兩個方法都過時了,MSDN的解釋是這樣: 警告: Suspend and Resume methods to synchronize the activities of threads." data-guid="ff8e76fddd4f9d11989c7d25f5e342d1"&gt;不要使用 Suspend 和 Resume 方法來同步線程的活動。您無法知道掛起線程時它正在執行什么代碼。AppDomain might be blocked." data-guid="7c4587074f4e52d11eadc59e58a09a57"&gt;如果您在安全權限評估期間掛起持有鎖的線程,則 AppDomain中的其他線程可能被阻止。AppDomain that attempt to use that class are blocked." data-guid="f164549efd95a0a94479d4d1c0d1ceec"&gt;如果您在線程正在執行類構造函數時掛起它,則 AppDomain中嘗試使用該類的其他線程將被阻止。這樣很容易發生死鎖。 對于這個解釋可能有點抽象吧,讓我們來看看一段代碼可能會清晰點: ``` class Program { static void Main(string[] args) { // 創建一個線程來測試 Thread thread1 = new Thread(TestMethod); thread1.Name = "Thread1"; thread1.Start(); Thread.Sleep(2000); Console.WriteLine("Main Thread is running"); ////int b = 0; ////int a = 3 / b; ////Console.WriteLine(a); thread1.Resume(); Console.Read(); } private static void TestMethod() { Console.WriteLine("Thread: {0} has been suspended!", Thread.CurrentThread.Name); //將當前線程掛起 Thread.CurrentThread.Suspend(); Console.WriteLine("Thread: {0} has been resumed!", Thread.CurrentThread.Name); } } ``` 在上面這段代碼中thread1線程是在主線程中恢復的,但當主線程發生異常時,這時候就thread1一直處于掛起狀態,此時thread1所使用的資源就不能釋放(除非強制終止進程),當另外線程需要使用這快資源的時候, 這時候就很可能發生死鎖現象。 上面一段代碼還存在一個隱患,請看下面一小段代碼: ``` class Program { static void Main(string[] args) { // 創建一個線程來測試 Thread thread1 = new Thread(TestMethod); thread1.Name = "Thread1"; thread1.Start(); Console.WriteLine("Main Thread is running"); thread1.Resume(); Console.Read(); } private static void TestMethod() { Console.WriteLine("Thread: {0} has been suspended!", Thread.CurrentThread.Name); Thread.Sleep(1000); //將當前線程掛起 Thread.CurrentThread.Suspend(); Console.WriteLine("Thread: {0} has been resumed!", Thread.CurrentThread.Name); } } ``` 當主線程跑(運行)的太快,做完自己的事情去喚醒thread1時,此時thread1還沒有掛起而起喚醒thread1,此時就會出現異常了。并且上面使用的Suspend和Resume方法,編譯器已經出現警告了,提示這兩個方法已經過時, 所以在我們平時使用中應該盡量避免。 **1.2 Abort和 Interrupt方法** Abort方法和Interrupt都是用來終止線程的,但是兩者還是有區別的。 1、他們拋出的異常不一樣,Abort 方法拋出的異常是ThreadAbortException, Interrupt拋出的異常為ThreadInterruptedException 2、調用interrupt方法的線程之后可以被喚醒,然而調用Abort方法的線程就直接被終止不能被喚醒的。 下面一段代碼是掩飾Abort方法的使用 ``` using System; using System.Threading; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Thread abortThread = new Thread(AbortMethod); abortThread.Name = "Abort Thread"; abortThread.Start(); Thread.Sleep(1000); try { abortThread.Abort(); } catch { Console.WriteLine("{0} Exception happen in Main Thread", Thread.CurrentThread.Name); Console.WriteLine("{0} Status is:{1} In Main Thread ", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState); } finally { Console.WriteLine("{0} Status is:{1} In Main Thread ", abortThread.Name, abortThread.ThreadState); } abortThread.Join(); Console.WriteLine("{0} Status is:{1} ", abortThread.Name, abortThread.ThreadState); Console.Read(); } private static void AbortMethod() { try { Thread.Sleep(5000); } catch(Exception e) { Console.WriteLine(e.GetType().Name); Console.WriteLine("{0} Exception happen In Abort Thread", Thread.CurrentThread.Name); Console.WriteLine("{0} Status is:{1} In Abort Thread ", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState); } finally { Console.WriteLine("{0} Status is:{1} In Abort Thread", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState); } } } ``` 運行結果: ![](https://box.kancloud.cn/2016-01-23_56a2eb31d99da.png) 從運行結果可以看出,調用Abort方法的線程引發的異常類型為ThreadAbortException, 以及異常只會在 調用Abort方法的線程中發生,而不會在主線程中拋出,并且調用Abort方法后線程的狀態不是立即改變為Aborted狀態,而是從AbortRequested-&gt;Aborted。 Interrupt方法: ``` using System; using System.Threading; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Thread interruptThread = new Thread(AbortMethod); interruptThread.Name = "Interrupt Thread"; interruptThread.Start(); interruptThread.Interrupt(); interruptThread.Join(); Console.WriteLine("{0} Status is:{1} ", interruptThread.Name, interruptThread.ThreadState); Console.Read(); } private static void AbortMethod() { try { Thread.Sleep(5000); } catch(Exception e) { Console.WriteLine(e.GetType().Name); Console.WriteLine("{0} Exception happen In Interrupt Thread", Thread.CurrentThread.Name); Console.WriteLine("{0} Status is:{1} In Interrupt Thread ", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState); } finally { Console.WriteLine("{0} Status is:{1} In Interrupt Thread", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState); } } } } ``` 運行結果: ![](https://box.kancloud.cn/2016-01-23_56a2eb31e8e39.png) 從結果中可以得到,調用Interrupt方法拋出的異常為:ThreadInterruptException, 以及當調用Interrupt方法后線程的狀態應該是中斷的, 但是從運行結果看此時的線程因為了Join,Sleep方法而喚醒了線程,為了進一步解釋調用Interrupt方法的線程可以被喚醒, 我們可以在線程執行的方法中運用循環,如果線程可以喚醒,則輸出結果中就一定會有循環的部分,然而調用Abort方法線程就直接終止,就不會有循環的部分,下面代碼相信大家看后肯定會更加理解兩個方法的區別的: ``` using System; using System.Threading; namespace ConsoleApplication2 { class Program { static void Main(string[] args) { Thread thread1 = new Thread(TestMethod); thread1.Start(); Thread.Sleep(100); thread1.Interrupt(); Thread.Sleep(3000); Console.WriteLine("after finnally block, the Thread1 status is:{0}", thread1.ThreadState); Console.Read(); } private static void TestMethod() { for (int i = 0; i < 4; i++) { try { Thread.Sleep(2000); Console.WriteLine("Thread is Running"); } catch (Exception e) { if (e != null) { Console.WriteLine("Exception {0} throw ", e.GetType().Name); } } finally { Console.WriteLine("Current Thread status is:{0} ", Thread.CurrentThread.ThreadState); } } } } } ``` 運行結果為: ![](https://box.kancloud.cn/2016-01-23_56a2eb3204222.png) 如果把上面的 thread1.Interrupt();改為 thread1.Abort(); 運行結果為: ![](https://box.kancloud.cn/2016-01-23_56a2eb32147f4.png) **二、線程池基礎** 首先,創建和銷毀線程是一個要耗費大量時間的過程,另外,太多的線程也會浪費內存資源,所以通過Thread類來創建過多的線程反而有損于性能,為了改善這樣的問題 ,.net中就引入了線程池。 線程池形象的表示就是存放應用程序中使用的線程的一個集合(就是放線程的地方,這樣線程都放在一個地方就好管理了)。CLR初始化時,線程池中是沒有線程的,在內部, 線程池維護了一個操作請求隊列,當應用程序想執行一個異步操作時,就調用一個方法,就將一個任務放到線程池的隊列中,線程池中代碼從隊列中提取任務,將這個任務委派給一個線程池線程去執行,當線程池線程完成任務時,線程不會被銷毀,而是返回到線程池中,等待響應另一個請求。由于線程不被銷毀, 這樣就可以避免因為創建線程所產生的性能損失。 **注意:通過線程池創建的線程默認為后臺線程,優先級默認為Normal.** **三、通過線程池的工作者線程實現異步** **3.1 創建工作者線程的方法** public static bool QueueUserWorkItem (WaitCallback callBack); public static bool QueueUserWorkItem(WaitCallback callback, Object state); 這兩個方法向線程池的隊列添加一個工作項(work item)以及一個可選的狀態數據。然后,這兩個方法就會立即返回。 工作項其實就是由callback參數標識的一個方法,該方法將由線程池線程執行。同時寫的回調方法必須匹配System.Threading.WaitCallback委托類型,定義為: public delegate void WaitCallback(Object state); 下面演示如何通過線程池線程來實現異步調用: ``` using System; using System.Threading; namespace ThreadPoolUse { class Program { static void Main(string[] args) { // 設置線程池中處于活動的線程的最大數目 // 設置線程池中工作者線程數量為1000,I/O線程數量為1000 ThreadPool.SetMaxThreads(1000, 1000); Console.WriteLine("Main Thread: queue an asynchronous method"); PrintMessage("Main Thread Start"); // 把工作項添加到隊列中,此時線程池會用工作者線程去執行回調方法 ThreadPool.QueueUserWorkItem(asyncMethod); Console.Read(); } // 方法必須匹配WaitCallback委托 private static void asyncMethod(object state) { Thread.Sleep(1000); PrintMessage("Asynchoronous Method"); Console.WriteLine("Asynchoronous thread has worked "); } // 打印線程池信息 private static void PrintMessage(String data) { int workthreadnumber; int iothreadnumber; // 獲得線程池中可用的線程,把獲得的可用工作者線程數量賦給workthreadnumber變量 // 獲得的可用I/O線程數量給iothreadnumber變量 ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber); Console.WriteLine("{0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n", data, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsBackground.ToString(), workthreadnumber.ToString(), iothreadnumber.ToString()); } } } ``` 運行結果: ![](https://box.kancloud.cn/2016-01-23_56a2eb3224e17.png) 從結果中可以看出,線程池中的可用的工作者線程少了一個,用去執行回調方法了。 ThreadPool.QueueUserWorkItem(WaitCallback callback,Object state) 方法可以把object對象作為參數傳送到回調函數中,使用和ThreadPool.QueueUserWorkItem(WaitCallback callback)的使用和類似,這里就不列出了。 **3.2** **協作式取消** .net Framework提供了**取消操作**的模式, 這個模式是協作式的。為了取消一個操作,首先必須創建一個**System.Threading.CancellationTokenSource**對象。 下面代碼演示了協作式取消的使用,主要實現當用戶在控制臺敲下回車鍵后就停止數數方法。 ``` using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace ConsoleApplication3 { class Program { static void Main(string[] args) { ThreadPool.SetMaxThreads(1000, 1000); Console.WriteLine("Main thread run"); PrintMessage("Start"); Run(); Console.ReadKey(); } private static void Run() { CancellationTokenSource cts = new CancellationTokenSource(); // 這里用Lambda表達式的方式和使用委托的效果一樣的,只是用了Lambda后可以少定義一個方法。 // 這在這里就是讓大家明白怎么lambda表達式如何由委托轉變的 ////ThreadPool.QueueUserWorkItem(o => Count(cts.Token, 1000)); ThreadPool.QueueUserWorkItem(callback, cts.Token); Console.WriteLine("Press Enter key to cancel the operation\n"); Console.ReadLine(); // 傳達取消請求 cts.Cancel(); } private static void callback(object state) { Thread.Sleep(1000); PrintMessage("Asynchoronous Method Start"); CancellationToken token =(CancellationToken)state; Count(token, 1000); } // 執行的操作,當受到取消請求時停止數數 private static void Count(CancellationToken token,int countto) { for (int i = 0; i < countto; i++) { if (token.IsCancellationRequested) { Console.WriteLine("Count is canceled"); break; } Console.WriteLine(i); Thread.Sleep(300); } Console.WriteLine("Cout has done"); } // 打印線程池信息 private static void PrintMessage(String data) { int workthreadnumber; int iothreadnumber; // 獲得線程池中可用的線程,把獲得的可用工作者線程數量賦給workthreadnumber變量 // 獲得的可用I/O線程數量給iothreadnumber變量 ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber); Console.WriteLine("{0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n", data, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsBackground.ToString(), workthreadnumber.ToString(), iothreadnumber.ToString()); } } } ``` 運行結果: ![](https://box.kancloud.cn/2016-01-23_56a2eb3242df9.png) **四、使用委托實現異步** 通過調用ThreadPool的QueueUserWorkItem方法來來啟動工作者線程非常方便,但委托WaitCallback指向的是帶有一個參數的無返回值的方法,如果我們實際操作中需要有返回值,或者需要帶有多個參數, 這時通過這樣的方式就難以實現, 為了解決這樣的問題,我們可以通過委托來建立工作這線程, 下面代碼演示了使用委托如何實現異步: ``` using System; using System.Threading; namespace Delegate { class Program { // 使用委托的實現的方式是使用了異步變成模型APM(Asynchronous Programming Model) // 自定義委托 private delegate string MyTestdelegate(); static void Main(string[] args) { ThreadPool.SetMaxThreads(1000, 1000); PrintMessage("Main Thread Start"); //實例化委托 MyTestdelegate testdelegate = new MyTestdelegate(asyncMethod); // 異步調用委托 IAsyncResult result = testdelegate.BeginInvoke(null, null); // 獲取結果并打印出來 string returndata = testdelegate.EndInvoke(result); Console.WriteLine(returndata); Console.ReadLine(); } private static string asyncMethod() { Thread.Sleep(1000); PrintMessage("Asynchoronous Method"); return "Method has completed"; } // 打印線程池信息 private static void PrintMessage(String data) { int workthreadnumber; int iothreadnumber; // 獲得線程池中可用的線程,把獲得的可用工作者線程數量賦給workthreadnumber變量 // 獲得的可用I/O線程數量給iothreadnumber變量 ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber); Console.WriteLine("{0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n", data, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsBackground.ToString(), workthreadnumber.ToString(), iothreadnumber.ToString()); } } } ``` 運行結果: ![](https://box.kancloud.cn/2016-01-23_56a2eb32583be.png) **五、任務** 同樣 任務的引入也是為了解決通過ThreadPool.QueueUserWorkItem中限制的問題, 下面代碼演示通過任務來實現異步: **5.1 使用任務來實現異步** ``` using System; using System.Threading; using System.Threading.Tasks; namespace TaskUse { class Program { static void Main(string[] args) { ThreadPool.SetMaxThreads(1000, 1000); PrintMessage("Main Thread Start"); // 調用構造函數創建Task對象, Task<int> task = new Task<int>(n => asyncMethod((int)n), 10); // 啟動任務 task.Start(); // 等待任務完成 task.Wait(); Console.WriteLine("The Method result is: "+task.Result); Console.ReadLine(); } private static int asyncMethod(int n) { Thread.Sleep(1000); PrintMessage("Asynchoronous Method"); int sum = 0; for (int i = 1; i < n; i++) { // 如果n太大,使用checked使下面代碼拋出異常 checked { sum += i; } } return sum; } // 打印線程池信息 private static void PrintMessage(String data) { int workthreadnumber; int iothreadnumber; // 獲得線程池中可用的線程,把獲得的可用工作者線程數量賦給workthreadnumber變量 // 獲得的可用I/O線程數量給iothreadnumber變量 ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber); Console.WriteLine("{0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n", data, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsBackground.ToString(), workthreadnumber.ToString(), iothreadnumber.ToString()); } } } ``` 運行結果: ![](https://box.kancloud.cn/2016-01-23_56a2eb326b304.png) **5.2 取消任務** 如果要取消任務, 同樣可以使用一個CancellationTokenSource對象來取消一個Task. 下面代碼演示了如何來取消一個任務: ``` using System; using System.Threading; using System.Threading.Tasks; namespace TaskUse { class Program { static void Main(string[] args) { ThreadPool.SetMaxThreads(1000, 1000); PrintMessage("Main Thread Start"); CancellationTokenSource cts = new CancellationTokenSource(); // 調用構造函數創建Task對象,將一個CancellationToken傳給Task構造器從而使Task和CancellationToken關聯起來 Task<int> task = new Task<int>(n => asyncMethod(cts.Token, (int)n), 10); // 啟動任務 task.Start(); // 延遲取消任務 Thread.Sleep(3000); // 取消任務 cts.Cancel(); Console.WriteLine("The Method result is: " + task.Result); Console.ReadLine(); } private static int asyncMethod(CancellationToken ct, int n) { Thread.Sleep(1000); PrintMessage("Asynchoronous Method"); int sum = 0; try { for (int i = 1; i < n; i++) { // 當CancellationTokenSource對象調用Cancel方法時, // 就會引起OperationCanceledException異常 // 通過調用CancellationToken的ThrowIfCancellationRequested方法來定時檢查操作是否已經取消, // 這個方法和CancellationToken的IsCancellationRequested屬性類似 ct.ThrowIfCancellationRequested(); Thread.Sleep(500); // 如果n太大,使用checked使下面代碼拋出異常 checked { sum += i; } } } catch (Exception e) { Console.WriteLine("Exception is:" + e.GetType().Name); Console.WriteLine("Operation is Canceled"); } return sum; } // 打印線程池信息 private static void PrintMessage(String data) { int workthreadnumber; int iothreadnumber; // 獲得線程池中可用的線程,把獲得的可用工作者線程數量賦給workthreadnumber變量 // 獲得的可用I/O線程數量給iothreadnumber變量 ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber); Console.WriteLine("{0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n", data, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsBackground.ToString(), workthreadnumber.ToString(), iothreadnumber.ToString()); } } } ``` 運行結果: ![](https://box.kancloud.cn/2016-01-23_56a2eb327b395.png) **5.3 任務工廠** 同樣可以通過任務工廠TaskFactory類型來實現異步操作。 ``` using System; using System.Threading; using System.Threading.Tasks; namespace TaskFactory { class Program { static void Main(string[] args) { ThreadPool.SetMaxThreads(1000, 1000); Task.Factory.StartNew(() => PrintMessage("Main Thread")); Console.Read(); } // 打印線程池信息 private static void PrintMessage(String data) { int workthreadnumber; int iothreadnumber; // 獲得線程池中可用的線程,把獲得的可用工作者線程數量賦給workthreadnumber變量 // 獲得的可用I/O線程數量給iothreadnumber變量 ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber); Console.WriteLine("{0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n", data, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsBackground.ToString(), workthreadnumber.ToString(), iothreadnumber.ToString()); } } } ``` 運行結果: ![](https://box.kancloud.cn/2016-01-23_56a2eb328e308.png) 講到這里CLR的工作者線程大致講完了,希望也篇文章可以讓大家對線程又有進一步的理解。在后面的一篇線程系列將談談CLR線程池的I/O線程。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看