20181104_C#线程之Thread_ThreadPool_使用Thread实现回到和带参数的回调
C#?? .net? Framework多線程演變路徑:
1.0??? 1.1 時代使用Thread
2.0??? 時代使用ThreadPool
3.0??? 時代使用Task
4.0??? 時代使用Parallel
4.5 時代使用 async/awit
一.? ?DoSomethingLong方法如下:
/// <summary>/// 一個比較耗時耗資源的私有方法/// </summary>/// <param name="name"></param>private void DoSomethingLong(string name){Console.WriteLine($"****************DoSomethingLong {name} Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");long lResult = 0;for (int i = 0; i < 1000000000; i++){lResult += i;}//Thread.Sleep(2000);Console.WriteLine($"****************DoSomethingLong {name} End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***************");}?
二.?? 使用Thread
a)???????? 下面代碼演示, 如何使用Thread來啟動一個線程
//public Thread(ThreadStart start, int maxStackSize); //maxStackSize 表示指定這個線程最大可以使用多少內(nèi)存空間, 一般不用設(shè)置ThreadStart threadStart = () => this.DoSomethingLong("btnThreads_Click");Thread thread = new Thread(threadStart); thread.Start();b) Thread的一些其它api
i. thread.Suspend();//(棄用)線程掛起, 使線程暫停執(zhí)行; 已過期, 不推薦使用, 會導致死鎖, 因為線程的執(zhí)行的時候, 是會占用資源的, 雖然手動讓線程暫停執(zhí)行了, 但是它占用的資源是不會釋放的 ii. thread.Resume();//(棄用)喚醒線程, 使掛起的線程重新開始執(zhí)行, 和Suspend()對應 iii. thread.Abort(); //線程終止try{thread.Abort();//銷毀,方式是拋異常 也不建議再使用 不一定及時/有些動作發(fā)出收不回來(比如子線程向數(shù)據(jù)庫發(fā)出一個查詢命令, 此時命令已經(jīng)發(fā)出, 但是數(shù)據(jù)庫還沒有返回來值, 但是主線程強制子線程停止了, 然后數(shù)據(jù)庫返回來的值就沒有人接收了)//就像你正在跑步, 人后旁邊有人說停; 你從聽到停, 到真正的停下來, 還是處于跑的狀態(tài)// 使用abort一定要使用try catch來處理}catch (Exception){//Thread.ResetAbort();//取消Abort異常, 然后繼續(xù)計算}//線程等待 iv. thread.Join();//當前線程等待thread完成, 當前線程就是誰執(zhí)行這句話, 誰就是當前線程, 在這里當前線程就是主線程了, 因為主線程在執(zhí)行這句話thread.Join(500);//最多等500; 當前線程等待500毫秒Console.WriteLine("等待500ms");// ThreadState.Running //啟動線程while (thread.ThreadState != ThreadState.Stopped){Thread.Sleep(100);//當前線程 休息100ms } v. 關(guān)于jion和sleep //jion是實實在在的等待, 占據(jù)cpu; 像上面的示例, thread.jion(500), 那么這個時候, 其實thread還是在做自己的事情. jion就會在這里等一段時間(或者等thread把活干完); 此時是有兩個線程并發(fā)運行的; 一個thread的線程, 一個jion的線程//sleep表示睡眠, 把當前線程的上下文保存著, 把cpu的時間片交出去, cpu可以去處理其他事情vi. 前臺線程和后臺線程的區(qū)別Console.WriteLine(thread.IsBackground);默認是前臺線程,啟動之后一定要完成任務(wù)的,阻止進程退出; 也就是說, 就算你的應用程序被關(guān)閉了, 但是前臺線程還是要堅持把它的事情做完之后才會停止自己thread.IsBackground = true;//指定后臺線程:隨著進程退出, 也就是說程序關(guān)閉后, 后臺線程也停止了vii. 線程優(yōu)先級:thread.Priority = ThreadPriority.Highest;//線程優(yōu)先級 CPU會優(yōu)先執(zhí)行標記為Highest的線程, 但是并不代表說Highest就一定最先把事情處理完, 只能說CPU會優(yōu)先為這個線程分配時間片?
三.?? 使用ThreadPool
a)???????? 推出ThreadPool的原因:
i. 在thread中提供了太多的API, 又是掛起, 又是終止, 又是休眠, 又是優(yōu)先級, 各種各樣亂七八糟, 但是又不是真真正正的真的能準確的操控 ii. 于是就到2.0之后,Thread就被替換成了ThreadPool, 把所有的該簡化的都簡化了,什么API都沒有了. 也沒有銷毀, 也沒有掛起, 更沒有暫停; 但是也可以將線程進行重用, 避免重復的創(chuàng)建和銷毀, 線程被使用完之后, 會放回池子, 下次繼續(xù)使用?
b)???????? 使用線程池ThreadPool的方式啟動多線程, 代碼如下:
//池→容器; 線程池就是線程的容器, 當在一個系統(tǒng)中, 反復的使用不同的資源的時候, 但是這些資源使用完需要二次創(chuàng)建(銷毀)的成本太高的時候, 就要考慮使用池技術(shù)//池技術(shù)就是享元模式的精華//QueueUserWorkItem隊列用戶工作項, 將用戶的工作項, 放入到隊列匯總//QueueUserWorkItem從線程池的方式來啟動多線程; 這可能是啟動多線程最簡單的一種方式了ThreadPool.QueueUserWorkItem(t => this.DoSomethingLong("btnThreadPool_Click"));ThreadPool.QueueUserWorkItem(t => this.DoSomethingLong("btnThreadPool_Click"));c)???????? 設(shè)置/獲取 ThreadPool中最大和最小線程數(shù)量:
{//對線程加以限制 workerThreads→線程池中最大的工作線程數(shù)據(jù)//workerThreads →線程池中的最大線程數(shù), 這個是工作線程, 平時啟動的線程一般都是基于工作線程的, 如果超過了將會被排隊//completionPortThreads表示線程池中異步i/o線程的最大數(shù)目ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads);Console.WriteLine($"GetMaxThreads workerThreads={workerThreads} completionPortThreads={completionPortThreads}");}{//線程池內(nèi)最小線程數(shù); 默認情況下, 最小的線程數(shù), 好像是跟CPU有關(guān); 比如4核8線程的, 那么這里就是8和8; 我的就是4和4ThreadPool.GetMinThreads(out int workerThreads, out int completionPortThreads);Console.WriteLine($"GetMinThreads workerThreads={workerThreads} completionPortThreads={completionPortThreads}");} // 有g(shù)et就有set 設(shè)置最大線程數(shù) ThreadPool的最大線程數(shù)也會影響這Task的線程ThreadPool.SetMaxThreads(16, 16);// 設(shè)置最小線程數(shù)ThreadPool.SetMinThreads(8, 8); //實際操作來看, 最小的會受到影響 //設(shè)置完成后, 再次打印一次{ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads);Console.WriteLine($"GetMaxThreads workerThreads={workerThreads} completionPortThreads={completionPortThreads}");}{ThreadPool.GetMinThreads(out int workerThreads, out int completionPortThreads);Console.WriteLine($"GetMinThreads workerThreads={workerThreads} completionPortThreads={completionPortThreads}");}
d)???????? 在ThreadPool中實現(xiàn)線程等待
ThreadPool啥API都沒有, 那么如何在ThreadPool中等待線程完成才往下執(zhí)行呢?// 可以使用 ManualResetEvent(手動重啟事件)類, 這個類包含了一個bool屬性, 如果在初始化這個類的時候, 將其初始化為false, 則這個類實例的的WaitOne()方法將會被阻塞, 一直阻塞, 直到他的bool屬性變成true; 當然, 可以通過ManualResetEvent的set方法使其變成 true ; 當然這里也可以自己定義一個變量來實現(xiàn), 但是ManualResetEvent是線程安全的// false--WaitOne等待--Set--true--WaitOne直接過去// true--WaitOne直接過去--ReSet--false--WaitOne等待ManualResetEvent manualResetEvent = new ManualResetEvent(false); //初始化ManualResetEvent類中的變量為falseThreadPool.QueueUserWorkItem(t =>{Console.WriteLine("即將開始執(zhí)行DoSomethingLong函數(shù)");this.DoSomethingLong("btnThreadPool_Click");Console.WriteLine("執(zhí)行DoSomethingLong函數(shù)完畢");manualResetEvent.Set(); //將信號設(shè)置為true//manualResetEvent.Reset(); //將信號設(shè)置為false});manualResetEvent.WaitOne(); //阻塞當前線程; 如果當前manualResetEvent在主線程創(chuàng)建的, 那么就會阻塞主線程 但是要注意一般來說,不要阻塞線程池的線程
四.? ? 使用Thread完成回調(diào)和帶返回值的回調(diào)
a)???????? 使用Thread完成回調(diào), 代碼如下:
/// <summary>/// 演示線程的回調(diào); 啟動子線程計算--完成委托后,該線程去執(zhí)行后續(xù)回調(diào)委托 ; /// </summary>/// <param name="act">第一個委托是你真的想要執(zhí)行的這個方法</param>/// <param name="callback">第二委托是當你執(zhí)行完第一個方法之后, 想要執(zhí)行的回調(diào), 其實就是在一個線程內(nèi)將兩個方法并列執(zhí)行了一次</param>private void ThreadWithCallback(Action act, Action callback){Thread thread = new Thread(() =>{act.Invoke();callback.Invoke();});thread.Start();}調(diào)用方法如下:
private void btnThreads_Click(object sender, EventArgs e){ this.ThreadWithCallback(() => Console.WriteLine($"這里是action {Thread.CurrentThread.ManagedThreadId.ToString("00")}"), () => Console.WriteLine($"這里是callback {Thread.CurrentThread.ManagedThreadId.ToString("00")}")); }
b)???????? 使用Thread完成帶返回值的回調(diào), 代碼如下:
/// <summary>/// 帶返回值的異步調(diào)用; 帶返回的異步調(diào)用 需要獲取返回值 (會卡界面的)/// </summary>/// <typeparam name="T"></typeparam>/// <param name="func"></param>/// <returns></returns>private Func<T> ThreadWithReturn<T>(Func<T> func){#region 錯誤的寫法//T t ;//Thread thread = new Thread(() =>//{// t = func.Invoke(); //這里還沒有執(zhí)行的時候, 它已經(jīng)返回了//});//thread.Start();// return t;#endregionT t = default(T);Thread thread = new Thread(() =>{t = func.Invoke();});thread.Start();return () =>{// while (thread.ThreadState != ThreadState.Stopped) { Thread.Sleep(200); }thread.Join();return t;};}
調(diào)用方法如下:
private void withReturn_Click(object sender, EventArgs e){Func<int> func = this.ThreadWithReturn<int>(() =>{Thread.Sleep(2000);return DateTime.Now.Millisecond;});Console.WriteLine("上面是異步調(diào)用, 直接就會打印這句話, 這句話不會等待2秒");int iResult = func.Invoke();Console.WriteLine(iResult); }?
?
轉(zhuǎn)載于:https://www.cnblogs.com/wxylog/p/9904977.html
總結(jié)
以上是生活随笔為你收集整理的20181104_C#线程之Thread_ThreadPool_使用Thread实现回到和带参数的回调的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 织梦正则批量替换文章内容内链变成绝对路径
- 下一篇: 基于C#局域网语音聊天