日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

13异步多线程(三)Parallel,线程安全

發布時間:2023/12/18 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 13异步多线程(三)Parallel,线程安全 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一.Parallel

和Task很像,啟動多個線程計算,但是主線程也參與計算,所以它也會卡界面。它本質上相當于Task+WaitAll,只要用到了Task開啟多個線程并且要WaitAll,就可以用Parallel,還可以節約一個線程(主線程參與運算)。

代碼如下:

Parallel.Invoke(() => this.DoSomethingLong("btnParallel_Click_002"),() => this.DoSomethingLong("btnParallel_Click_001"),() => this.DoSomethingLong("btnParallel_Click_003"),() => this.DoSomethingLong("btnParallel_Click_004"),() => this.DoSomethingLong("btnParallel_Click_005"));

返回結果如下:

Parallel還有一些API,比如:

Parallel.For(0, 5, t => {this.DoSomethingLong($"btnParallel_Click_00{t}"); });

這段代碼借助了for循環,和上面代碼寫的是一樣的。還可以這樣寫:

Parallel.ForEach(new int[] { 0, 1, 2, 3, 4 }, t => {this.DoSomethingLong($"btnParallel_Click_00{t}"); });

這里有個問題,如果要循環100次請求,有100個線程給我們用嗎?當然不會的,這時候要設定一個開啟線程并發的最大值,開啟的線程只有執行完了,下一個線程才可以執行,可以這樣寫:

ParallelOptions options = new ParallelOptions() {MaxDegreeOfParallelism = 3 //最大3個線程并發任務 };Parallel.ForEach(new int[] { 0, 1, 2, 3, 4 }, options, t => {this.DoSomethingLong($"btnParallel_Click_00{t}"); });

執行結果如下:

?

如果在執行過程中還需要人工干預的話,可以加上state,并對state進行stop,break操作,比如下面:

Parallel.ForEach(new int[] { 0, 1, 2, 3, 4 }, options, (t, state) =>{this.DoSomethingLong($"btnParallel_Click_00{t}");//state.Stop();//結束全部的//state.Break();//停止當前的//return;});

?

二.多線程的異常處理

?

private void btnThreadCore_Click(object sender, EventArgs e){try{TaskFactory taskFactory = new TaskFactory();List<Task> taskList = new List<Task>();for (int i = 0; i < 20; i++){string name = string.Format($"btnThreadCore_Click_{i}");Action<object> act = t =>{//多線程內部要加try catch,處理自己的異常try{Thread.Sleep(2000);//遇上11,則會拋出異常,顯示“執行失敗”if (t.ToString().Equals("btnThreadCore_Click_11")){throw new Exception(string.Format($"{t} 執行失敗"));}//遇上12,則會拋出異常,顯示“執行失敗”if (t.ToString().Equals("btnThreadCore_Click_12")){throw new Exception(string.Format($"{t} 執行失敗"));}Console.WriteLine("{0} 執行成功", t);}catch (Exception ex){Console.WriteLine(ex.Message);}};taskList.Add(taskFactory.StartNew(act, name));}}catch (AggregateException aex){foreach (var item in aex.InnerExceptions){Console.WriteLine(item.Message);}}catch (Exception ex){Console.WriteLine(ex.Message);}}

顯示結果:

?

好了,如果我們內部不抓取catch,放到外面去抓catch呢?代碼這樣的:

private void btnThreadCore_Click(object sender, EventArgs e){try{TaskFactory taskFactory = new TaskFactory();List<Task> taskList = new List<Task>();for (int i = 0; i < 20; i++){string name = string.Format($"btnThreadCore_Click_{i}");Action<object> act = t =>{Thread.Sleep(2000);if (t.ToString().Equals("btnThreadCore_Click_11")){throw new Exception(string.Format($"{t} 執行失敗"));}if (t.ToString().Equals("btnThreadCore_Click_12")){throw new Exception(string.Format($"{t} 執行失敗"));}Console.WriteLine("{0} 執行成功", t);};taskList.Add(taskFactory.StartNew(act, name));}}catch (AggregateException aex){foreach (var item in aex.InnerExceptions){Console.WriteLine(item.Message);}}catch (Exception ex){Console.WriteLine(ex.Message);}}

這樣是很危險的,異常不拋出,外面獲取不了,還以為全部執行成功了。

外面能不能獲取到異常呢?可以的,代碼如下:

private void btnThreadCore_Click(object sender, EventArgs e){try{TaskFactory taskFactory = new TaskFactory();List<Task> taskList = new List<Task>();for (int i = 0; i < 20; i++){string name = string.Format($"btnThreadCore_Click_{i}");Action<object> act = t =>{try{Thread.Sleep(2000);if (t.ToString().Equals("btnThreadCore_Click_11")){throw new Exception(string.Format($"{t} 執行失敗"));}if (t.ToString().Equals("btnThreadCore_Click_12")){throw new Exception(string.Format($"{t} 執行失敗"));}Console.WriteLine("{0} 執行成功", t);}catch (Exception ex){Console.WriteLine(ex.Message);}};taskList.Add(taskFactory.StartNew(act, name));}//就加這一句,意思是等待所有線程全部執行完Task.WaitAll(taskList.ToArray());}catch (AggregateException aex){foreach (var item in aex.InnerExceptions){Console.WriteLine(item.Message);}}catch (Exception ex){Console.WriteLine(ex.Message);}}

這樣就可以在委托外面獲取到異常了:

不過,還是不要這么做,最好用第一種方法,委托內部try catch,獲取異常,因為不知道外面有沒有

taskList.Add(taskFactory.StartNew(act, name));? ?這個語句。

三.取消線程

場景:多個線程并發,如果一個任務失敗就要求其他任務不再執行了。

線程取消不是操作線程,而是操作信號量(共享變量,多個線程都能訪問到的東西,變量/數據庫的數據/硬盤數據)。
每個線程在執行的過程中,經常去查看下這個信號量,然后自己結束自己,線程不能別人終止,只能自己干掉自己,延遲是少不了的。

CancellationTokenSource可以在cancel后,取消沒有啟動的任務

看一下執行結果:

?

四.多線程的臨時變量

再看一個,每個循環中定義一個K,并且等于i

五.線程安全

循環10000次,TotalCount每次加1,IntList集合每次新增一個元素,那么按照原計劃,打印出來,TotalCount=10000,IntLis.Count = 10000。運行后看結果:

不是預想的10000。因為是多線程,可能變量被幾個線程同時在修改,所以就少了,變量應該放在多線程內部,不要共享出去,這樣才是安全的。

解決多線程沖突第一個辦法:lock,lock的方法塊兒里面是單線程的;lock里面的代碼要盡量的少
先看lock:

lock是語法糖,?lock==Monitor.Enter,檢查下這個變量有沒有被lock? ?有就等著,沒有就占有,然后進去執行,執行完了釋放。

看一下怎樣用lock改造上面的代碼:

先定義一個讓lock鎖定的變量,是個靜態的

結果:

這樣就不會少了,lock對btnThreadCore_Click_Lock這個變量(變量必須是引用類型)進行鎖定,其他線程如果進來,發現btnThreadCore_Click_Lock被鎖定了,只有等待釋放,所以?lock塊中每次只允許進入一個線程,lock塊變成了單線程,既然lock塊變成了單線程,那么里面的代碼越少越好,早點執行完。

有兩個錯誤的使用lock的方法:

1.lock(this)?

鎖定了當前實例,別的地方如果要使用這個變量呢?都被鎖定了 ,如果每個實例想要單獨的鎖定 ,用 private object

2.鎖定一個字符串

比如?string a="123456"; lock(a)

a="123456",已經被內存中分配空間了,如果下次定義了b也等于"123456"(享元模式的內存分配,字符串是唯一的),string是引用類型,系統會把a和b當作同一個內存地址,而a已經被鎖定,相當于b也被鎖定了,如果此時有線程想鎖定b,就只能等待。

解決多線程沖突第二個辦法:沒有沖突,從數據上隔離開,即線程之間不要有共享數據,每個線程之間不要數據交叉,各自處理自己的數據,這樣就避免了沖突。

總結

以上是生活随笔為你收集整理的13异步多线程(三)Parallel,线程安全的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。