【转】1.DThread、ThreadPool、Task、Parallel的基本用法、区别以及弊端
多線程的操作在程序中也是比較常見的,比如開啟一個線程執(zhí)行一些比較耗時的操作(IO操作),而主線程繼續(xù)執(zhí)行當(dāng)前操作,不會造成主線程阻塞。線程又分為前臺線程和后臺線程,區(qū)別是:整個程序必須要運行完前臺線程才會退出,而后臺線程會在程序退出的時候結(jié)束掉。Thread默認(rèn)創(chuàng)建的是前臺線程,而ThreadPool和Task默認(rèn)創(chuàng)建的是后臺線程,Thread可以通過設(shè)置?IsBackground 屬性將線程設(shè)置為后臺線程。
static void Main(string[] args) {Thread thread = new Thread(new ThreadStart(NoParameterMethod));thread.Start();Console.WriteLine("程序已經(jīng)執(zhí)行完成"); }static void NoParameterMethod() {Thread.Sleep(1000);Console.WriteLine("NoParameterMethod"); }效果:
static void Main(string[] args) {Thread thread = new Thread(new ThreadStart(NoParameterMethod)){IsBackground = true};thread.Start();Console.WriteLine("程序已經(jīng)執(zhí)行完成"); }static void NoParameterMethod() {Thread.Sleep(1000);Console.WriteLine("NoParameterMethod"); }效果:
下面來說一下幾種開啟多線程的方法:
1、Thread
1.1 開啟一個線程,執(zhí)行一個不帶參數(shù)的方法
static void Main(string[] args) {Thread thread = new Thread(new ThreadStart(NoParameterMethod));//注意Start開啟線程之后,當(dāng)前線程不是說一定會立馬執(zhí)行//而是說當(dāng)前線程已經(jīng)準(zhǔn)備好被CPU調(diào)用,至于CPU什么時候調(diào)用是需要看情況而定thread.Start();Console.WriteLine("程序已經(jīng)執(zhí)行完成"); }static void NoParameterMethod() {//使當(dāng)前線程停止1sThread.Sleep(1000);Console.WriteLine("NoParameterMethod"); }1.2開啟一個線程,執(zhí)行帶參數(shù)的方法
static void Main(string[] args) {Thread thread = new Thread(new ParameterizedThreadStart(ParameterMethod));//要傳入的參數(shù)在Start的時候傳入thread.Start("ParameterMethod");Console.WriteLine("程序已經(jīng)執(zhí)行完成"); } //參數(shù)類型必須為Object類型,方法只能有一個參數(shù) //如果想傳入多個參數(shù),可已將參數(shù)封裝進(jìn)入一個類中 static void ParameterMethod(Object x) {Thread.Sleep(1000);Console.WriteLine(x); }2、ThreadPool
使用ThreadPool開啟一個線程
//無參 Thread.CurrentThread.ManagedThreadId是當(dāng)前線程的唯一標(biāo)識符 ThreadPool.QueueUserWorkItem(new WaitCallback(obj => Console.WriteLine(Thread.CurrentThread.ManagedThreadId))); //有參 ThreadPool.QueueUserWorkItem(new WaitCallback(obj => Console.WriteLine(Thread.CurrentThread.ManagedThreadId)), "參數(shù)");ThreadPool是Thread的一個升級版,ThreadPool是從線程池中獲取線程,如果線程池中又空閑的元素,則直接調(diào)用,如果沒有才會創(chuàng)建,而Thread則是會一直創(chuàng)建新的線程,要知道開啟一個線程就算什么事都不做也會消耗大約1m的內(nèi)存,是非常浪費性能的,接下來我們寫一個例子來看一下二者的區(qū)別:
#region 使用Thread開啟100個線程 for (int i = 0; i < 100; i++) {(new Thread(new ThreadStart(() => Console.WriteLine(Thread.CurrentThread.ManagedThreadId)))).Start(); } #endregion運行結(jié)果:
我們可以看到每一個主線程表示id都是不同的,也就是說使用Thread開啟線程每次都是新創(chuàng)建一個
#region 使用ThreadPool開啟100個線程 for (int i = 0; i < 100; i++) {ThreadPool.QueueUserWorkItem(new WaitCallback(obj => Console.WriteLine(Thread.CurrentThread.ManagedThreadId))); } #endregion運行結(jié)果:
相信區(qū)別已經(jīng)很明顯了,這里我再說一下,線程池中一開始是沒有一個線程的,使用ThreadPool開啟一個線程之后,線程執(zhí)行完畢,會加入到線程池中,后續(xù)需要再次開啟線程的時候查看線程池中有沒有空閑的線程,有則調(diào)用,沒有則創(chuàng)建,如此循環(huán)
二者之間還有一個區(qū)別,就是ThreadPool可以操控線程的狀態(tài),比如等待線程完成,或者終止超時子線程操作
取消子線程操作
CancellationTokenSource cts = new CancellationTokenSource(); ThreadPool.QueueUserWorkItem(new WaitCallback(CanCancelMethod),cts.Token); cts.Cancel(); Console.ReadKey(); static void CanCancelMethod(Object obj) {CancellationToken ct = (CancellationToken)obj;if (ct.IsCancellationRequested) {Console.WriteLine("該線程已取消");}//就算ct.IsCancellationRequested為真,接下來的代碼還是會執(zhí)行//因為該方法并沒有ruturnThread.Sleep(1000);Console.WriteLine($"子線程{Thread.CurrentThread.ManagedThreadId}結(jié)束"); }感覺這個取消子線程的方法和設(shè)置一個全局變量,然后通過判斷和更改全局變量的值,設(shè)置線程是否取消的效果一樣
ThreadPool的其他操作感興趣的可以自己搜索學(xué)一下,因為終止線程什么操作都是比較麻煩的,關(guān)于ThreadPool就不再多說了
3、Task
Task和ThreadPool是一樣的,都是從線程池中取空閑的線程
?使用Task開啟一個線程
//方法1 使用Task的Run方法 Task.Run(()=> {Console.WriteLine($"線程{Thread.CurrentThread.ManagedThreadId}已開啟"); }); //方法2 使用Task工廠類TaskFactory對象的StartNew方法 (new TaskFactory()).StartNew(() => {Console.WriteLine($"線程{Thread.CurrentThread.ManagedThreadId}已開啟"); });Run和StartNew方法都是返回一個Task類型的對象,代表當(dāng)前開啟的線程,如果方法有返回值
//如果方法有返回值 Task<int> t1 = Task.Run<int>(() => {return 1; }); //通過t1.Result查看返回的結(jié)果 Console.WriteLine(t1.Result);取消線程操作的話和ThreadPool取消線程操作一樣
//1s后自動取消線程 CancellationTokenSource cts = new CancellationTokenSource(1000); //為取消線程注冊回調(diào)函數(shù) cts.Token.Register(()=> {Console.WriteLine("線程已取消"); });Task.Run(()=> {Console.WriteLine("開始執(zhí)行");Thread.Sleep(2000);//判斷當(dāng)前線程是否已被取消if (cts.Token.IsCancellationRequested) {Console.WriteLine("方法已結(jié)束");return;}Console.WriteLine("線程繼續(xù)執(zhí)行"); },cts.Token);等待所有線程執(zhí)行完畢
//存放所有線程 List<Task> lst = new List<Task>(); //開啟10個線程 for (int i = 0;i < 10;i++) {lst.Add(Task.Run(()=> {Thread.Sleep(new Random().Next(1,3000));Console.WriteLine($"線程{Thread.CurrentThread.ManagedThreadId}");})); } //等待所有線程執(zhí)行完畢 Task.WaitAll(lst.ToArray()); Console.WriteLine("所有線程執(zhí)行完畢");等待任意一個先線程執(zhí)行完畢
//存放所有線程 List<Task> lst = new List<Task>(); //開啟10個線程 for (int i = 0;i < 10;i++) {lst.Add(Task.Run(()=> {Thread.Sleep(new Random().Next(1,3000));Console.WriteLine($"線程{Thread.CurrentThread.ManagedThreadId}");})); } //等待任意線程執(zhí)行完畢 Task.WaitAny(lst.ToArray()); Console.WriteLine("已有現(xiàn)成執(zhí)行完畢");對于Thread、ThreadPool和Task,如果要用多線程的話,優(yōu)先使用Task,如果版本不支持Task,則考慮ThreadPool
4、Parallel
Parallel循環(huán)開啟多線程,并行任務(wù),對于多線程開啟任務(wù),開啟的順序都是不確定的
Parallel.Invoke()
Action[] action = new Action[] {()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"), }; Parallel.Invoke(action);相當(dāng)于
Action[] action = new Action[] {()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"), }; for (int i = 0; i < action.Length; i++) {Task.Run(action[i]); }Invoke時也可以進(jìn)行一些配置,例如配置線程池中只能最多保持一個線程
Action[] action = new Action[] {()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"),()=>Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"), }; Parallel.Invoke(new ParallelOptions() {MaxDegreeOfParallelism = 1 }, action);運行結(jié)果:
Parallel.For()
//將迭代的結(jié)果保存起來 ParallelLoopResult plr = Parallel.For(1, 10, (i) => {Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}"); }); Console.WriteLine(plr.IsCompleted);相當(dāng)于
for (int i = 1; i < 10; i++) {Task.Run(() =>{Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}");}); }相對于循環(huán)Task.Run()更加簡潔
Parallel.ForEach()
方法和foreach類似,不過是采用的是異步方式遍歷,要想被Parallel.ForEach()必須實現(xiàn)IEnumerable接口
Parallel.ForEach<String>(new List<String>() {"a","b","c","d","e","f","g","h","i" }, (str) => {Console.WriteLine(str); });運行結(jié)果:
停止循環(huán)的方法
//將迭代的結(jié)果保存起來 ParallelLoopResult plr = Parallel.For(1, 10, (i,state) => {Console.WriteLine($"線程:{Thread.CurrentThread.ManagedThreadId}");if (i==4) {//結(jié)束state.Break();} }); Console.WriteLine(plr.IsCompleted);總結(jié)
以上是生活随笔為你收集整理的【转】1.DThread、ThreadPool、Task、Parallel的基本用法、区别以及弊端的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 油价破纪录 95号油进入“10元时代”!
- 下一篇: 新东方主播董宇辉爆火走红:本人自曝一度痛