一、Thread類
C#里面的多執行緒:Thread類是C#語言對執行緒物件的一個封裝,
首先看下如何開啟執行緒,執行委托的內容:
/// <summary> /// 一個比較耗時耗資源的私有方法 /// </summary> private void DoSomethingLong(string name) { Console.WriteLine($"****************DoSomethingLong Start {name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} " + $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************"); long lResult = 0; for (int i = 0; i < 1_000_000_000; i++) { lResult += i; } Thread.Sleep(2000); Console.WriteLine($"****************DoSomethingLong End {name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} " + $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***************"); }
/// <summary> /// 多執行緒 Thread類是.NET Framework 1.0的時候出現的 /// Thread:C#對執行緒物件的一個封裝 /// </summary> private void btnThread_Click(object sender, EventArgs e) { Console.WriteLine($"****************btnThread_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} " + $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************"); { ParameterizedThreadStart method = o => this.DoSomethingLong("btnThread_Click"); Thread thread = new Thread(method); thread.Start("浪子天涯");//開啟執行緒,執行委托的內容 } Console.WriteLine($"****************btnThread_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} " + $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************"); }
執行緒等待、執行緒優先級、前臺執行緒和后臺執行緒:
{ ThreadStart method = () => { Thread.Sleep(5000); this.DoSomethingLong("btnThread_Click"); Thread.Sleep(5000); }; Thread thread = new Thread(method); thread.Start(); //開啟執行緒,執行委托的內容 //該花括號內的這些方法已經被微軟拋棄了,建議不要去用 { //thread.Suspend(); //暫停 //thread.Resume();//恢復 真的不該要的,暫停不一定馬上暫停;讓執行緒操作太復雜了 //thread.Abort(); //執行緒是計算機資源,程式想停下執行緒,只能向作業系統通知(執行緒拋例外), //會有延時/不一定能真的停下來 //Thread.ResetAbort(); } //1等待 while (thread.ThreadState != ThreadState.Stopped) { Thread.Sleep(200); //當前執行緒休息200ms } //2 Join等待 thread.Join(); //運行這句代碼的執行緒,等待thread的完成 thread.Join(1000); //最多等待1000ms Console.WriteLine("這里是執行緒執行完之后才操作,,,"); //最高優先級:優先執行,但不代表優先完成看,甚至說極端情況下,還有意外發生,不能通過這個來控制執行緒的執行先后順序 thread.Priority = ThreadPriority.Highest; //是否是后臺執行緒 默認是false thread.IsBackground = false; //默認是false 前臺執行緒,行程關閉,執行緒需要計算完后才退出 //thread.IsBackground = true;//關閉行程,執行緒退出 }
下面來看下Thread類的使用:
基于Thread封裝一個帶有回呼的
/// <summary> /// 基于Thread封裝一個回呼 /// 回呼:啟動子執行緒執行動作A--不阻塞--A執行完后子執行緒會執行動作B /// </summary> /// <param name="threadStart">多執行緒執行的操作</param> /// <param name="actionCallback">執行緒完成后,回呼的動作</param> private void ThreadWithCallBack(ThreadStart threadStart, Action actionCallback) { //Thread thread = new Thread(threadStart); //thread.Start(); //thread.Join(); //錯了,因為方法被阻塞了 //actionCallback.Invoke(); ThreadStart method = new ThreadStart(() => { threadStart.Invoke(); actionCallback.Invoke(); }); new Thread(method).Start(); }
{ ThreadStart threadStart = () => this.DoSomethingLong("btnThread_Click"); Action actionCallBack = () => { Thread.Sleep(2000); Console.WriteLine($"This is Calllback {Thread.CurrentThread.ManagedThreadId.ToString("00")}"); }; this.ThreadWithCallBack(threadStart, actionCallBack); }
基于Thread封裝一個帶有回傳值的
/// <summary> /// 基于Thread封裝一個帶有回傳值的 /// 1 異步,非阻塞的 /// 2 還能獲取到最終計算結果 /// /// 既要不阻塞,又要計算結果?不可能!故此處回傳一個委托,當外部需要使用結果的時候再阻塞,此時可能已經計算完了, /// </summary> private Func<T> ThreadWithReturn<T>(Func<T> func) { T t = default(T); ThreadStart threadStart = new ThreadStart(() => { t = func.Invoke(); }); Thread thread = new Thread(threadStart); thread.Start(); return new Func<T>(() => { thread.Join(); //thread.ThreadState return t; }); }
{ Func<int> func = () => { Thread.Sleep(5000); return DateTime.Now.Year; }; Func<int> funcThread = this.ThreadWithReturn(func);//非阻塞 Console.WriteLine("do something 1"); Console.WriteLine("do something 2"); Console.WriteLine("do something 3"); int iResult = funcThread.Invoke();//阻塞 }
控制執行緒的數量(僅供參考):
{ List<Thread> threads = new List<Thread>(); for (int i = 0; i < 100; i++) { if (threads.Count(t => t.ThreadState == ThreadState.Running) < 10) { Thread thread = new Thread(new ThreadStart(() => { })); thread.Start(); threads.Add(thread); } else { Thread.Sleep(200); } } }
二、ThreadPool類
由于Thread類功能繁多,反而用不好--就像給4歲小孩一把熱武器,反而會造成更大的傷害,而且對執行緒數量也是沒有管控的,故微軟在.NET Framework 2.0推出來ThreadPool執行緒池,
如果某個物件創建和銷毀代價比較高,同時這個物件還可以反復使用的,就需要一個池子,
保存多個這樣的物件,需要用的時候從池子里面獲取;用完之后不用銷毀,放回池子;(享元模式)
節約資源提升性能;此外,還能管控總數量,防止濫用;
ThreadPool的執行緒都是后臺執行緒,
下面我們直接來看下相關代碼:
/// <summary> /// ThreadPool執行緒池 /// 由于Thread類功能繁多,反而用不好--就像給4歲小孩一把熱武器,反而會造成更大的傷害 /// 對執行緒數量是沒有管控的 /// /// 執行緒池是.NET Framework 2.0推出來的 /// 如果某個物件創建和銷毀代價比較高,同時這個物件還可以反復使用的,就需要一個池子 /// 保存多個這樣的物件,需要用的時候從池子里面獲取;用完之后不用銷毀,放回池子;(享元模式) /// 節約資源提升性能;此外,還能管控總數量,防止濫用; /// /// ThreadPool的執行緒都是后臺執行緒 /// /// 大家課后可以試試,基于ThreadPool去封裝回呼--回傳值的 /// </summary> private void btnThreadPool_Click(object sender, EventArgs e) { Console.WriteLine($"****************btnThreadPool_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} " + $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************"); //啟動執行緒 { ThreadPool.QueueUserWorkItem(o => this.DoSomethingLong("btnThreadPool_Click1")); ThreadPool.QueueUserWorkItem(o => this.DoSomethingLong("btnThreadPool_Click2"), "浪子天涯"); } { ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads); Console.WriteLine($"當前電腦最大workerThreads={workerThreads} 最大completionPortThreads={completionPortThreads}"); ThreadPool.GetMinThreads(out int workerThreadsMin, out int completionPortThreadsMin); Console.WriteLine($"當前電腦最小workerThreads={workerThreadsMin} 最大completionPortThreads={completionPortThreadsMin}"); //設定的執行緒池數量是行程全域的(慎用,一般不用) //委托異步呼叫--Task--Parrallel--async/await 全部都是執行緒池的執行緒 //直接new Thread不受這個數量限制的(但是會占用執行緒池的執行緒數量) ThreadPool.SetMaxThreads(8, 8); //設定的最大值,必須大于CPU核數,否則設定無效 ThreadPool.SetMinThreads(2, 2); Console.WriteLine("====================設定執行緒池數量最大最小===================="); ThreadPool.GetMaxThreads(out int workerThreads1, out int completionPortThreads1); Console.WriteLine($"當前電腦最大workerThreads={workerThreads1} 最大completionPortThreads={completionPortThreads1}"); ThreadPool.GetMinThreads(out int workerThreadsMin1, out int completionPortThreadsMin1); Console.WriteLine($"當前電腦最大workerThreads={workerThreadsMin1} 最大completionPortThreads={completionPortThreadsMin1}"); } //執行緒等待 { ManualResetEvent mre = new ManualResetEvent(false); //false---關閉---Set打開---true---WaitOne就能通過 //true---打開--ReSet關閉---false--WaitOne就只能等待 ThreadPool.QueueUserWorkItem(o => { this.DoSomethingLong("btnThreadPool_Click1"); mre.Set(); }); Console.WriteLine("Do Something 1"); Console.WriteLine("Do Something 2"); Console.WriteLine("Do Something 3"); mre.WaitOne(); Console.WriteLine("任務已經完成了,,,"); } //寫多執行緒的時候有這么一種說法:不要阻塞執行緒池里面的執行緒, //下面是一個死鎖的例子 { ThreadPool.SetMaxThreads(8, 8); ManualResetEvent mre = new ManualResetEvent(false); for (int i = 0; i < 10; i++) { int k = i; //此處必須宣告一個變數存放i的值,不能直接使用i變數,否則會有問題 ThreadPool.QueueUserWorkItem(t => { Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId.ToString("00")} show {k}"); if (k == 9) //設定了最多只允許8個執行緒,但此處是9,導致死鎖了 { mre.Set(); //開關打開 } else { mre.WaitOne(); //執行緒等待,阻塞 } }); } if (mre.WaitOne()) //開關沒打開,一直等待,死鎖了 { Console.WriteLine("任務全部執行成功!"); } } Console.WriteLine($"****************btnThreadPool_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} " + $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************"); }
Demo原始碼:
鏈接:https://pan.baidu.com/s/1wVscaka37emNGz9x-rm0qA 提取碼:3l2e
此文由博主精心撰寫轉載請保留此原文鏈接:https://www.cnblogs.com/xyh9039/p/13550714.html
著作權宣告:如有雷同純屬巧合,如有侵權請及時聯系本人修改,謝謝!!!
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/1603.html
標籤:ASP.NET
上一篇:.NET異步和多執行緒系列(一)
