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