C# 多线程之List的线程安全问题
網(wǎng)上關于List的線程安全問題將的很少,所以自己實驗了一把,發(fā)現(xiàn)確實是線程不安全的.所以當你在進行多線程編程中使用了共享的List集合,必須對其進行線程安全處理.
List的Add方法是線程不安全的,List的源碼中的Add方法,使用了每次當當前的元素達到上限,通過創(chuàng)建一個新的數(shù)組實例,并給長度翻倍的操作.如果單線程操作不會有問題,直接擴容,然后繼續(xù)往里面加值。下面是List的Add方法和核心邏輯.
也就是說,當多個線程同時添加元素,且剛好它們都執(zhí)行到了擴容這個階段,當一個線程擴大了這個數(shù)組的長度,且進行了+1操作后,另外一個線程剛好也在執(zhí)行擴容的操作,這個時候它給Capacity的值設為2048,但是另外一個線程已經將this._size設為2049了,所以這個時候就報異常了.當然不止這一個問題,還有Copy的時候也會出問題,如果里面的元素過多,另外一個線程拿到空值的幾率很大.
代碼重現(xiàn):
class Program{static List<long> list = new List<long>();static void Main(string[] args){var t = Task.Run(() =>{var tf = new TaskFactory(TaskCreationOptions.AttachedToParent, TaskContinuationOptions.AttachedToParent);var childTasks = new Task[]{tf.StartNew(()=>Task_0()),tf.StartNew(()=>Task_1()),tf.StartNew(()=>Task_2())};var tfTask=tf.ContinueWhenAll(childTasks, completedTasks => completedTasks.Where(w => !w.IsFaulted && !w.IsCanceled), TaskContinuationOptions.None);tfTask.ContinueWith(task=>{var a = list;});});Console.ReadKey();}static void Task_0(){for (var i = 0; i < 1000000; i++){list.Add(i);}}static void Task_1(){for (var i = 0; i < 1000000; i++){list.Add(i);}}static void Task_2(){for (var i = 0; i < 1000000; i++){list.Add(i);}}}多跑幾次這段代碼,你幾乎可以重現(xiàn)所有可能出現(xiàn)的多線程資源爭用異常.
解決方案:給Add方法加鎖,代碼如下:
?
class Program{static object lockObj = new object();static List<long> list = new List<long>();static void Main(string[] args){var t = Task.Run(() =>{var tf = new TaskFactory(TaskCreationOptions.AttachedToParent, TaskContinuationOptions.AttachedToParent);var childTasks = new Task[]{tf.StartNew(()=>Task_0()),tf.StartNew(()=>Task_1()),tf.StartNew(()=>Task_2())};var tfTask=tf.ContinueWhenAll(childTasks, completedTasks => completedTasks.Where(w => !w.IsFaulted && !w.IsCanceled), TaskContinuationOptions.None);tfTask.ContinueWith(task=>{var a = list;});});Console.ReadKey();}static void Task_0(){for (var i = 0; i < 1000000; i++){lock (lockObj){list.Add(i);}}}static void Task_1(){for (var i = 0; i < 1000000; i++){lock (lockObj){list.Add(i);}}}static void Task_2(){for (var i = 0; i < 1000000; i++){lock (lockObj){list.Add(i);}}}}ok,解決了問題,當然這不是最好的解決方案,你完全可以通過適配器模式,去擴展一個線程安全的List類型,這里我就不寫了.
?
轉載于:https://www.cnblogs.com/GreenLeaves/p/10092128.html
總結
以上是生活随笔為你收集整理的C# 多线程之List的线程安全问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: koa cookie使用
- 下一篇: jQuery源码分析系列 : 整体架构