C# 多线程之List的线程安全问题
网上关于List的线程安全问题将的很少,所以自己实验了一把,发现确实是线程不安全的.所以当你在进行多线程编程中使用了共享的List集合,必须对其进行线程安全处理.
List的Add方法是线程不安全的,List的源码中的Add方法,使用了每次当当前的元素达到上限,通过创建一个新的数组实例,并给长度翻倍的操作.如果单线程操作不会有问题,直接扩容,然后继续往里面加值。下面是List的Add方法和核心逻辑.


也就是说,当多个线程同时添加元素,且刚好它们都执行到了扩容这个阶段,当一个线程扩大了这个数组的长度,且进行了+1操作后,另外一个线程刚好也在执行扩容的操作,这个时候它给Capacity的值设为2048,但是另外一个线程已经将this._size设为2049了,所以这个时候就报异常了.当然不止这一个问题,还有Copy的时候也会出问题,如果里面的元素过多,另外一个线程拿到空值的几率很大.
代码重现:
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 = ; i < ; i++)
{
list.Add(i);
}
} static void Task_1()
{
for (var i = ; i < ; i++)
{
list.Add(i);
}
} static void Task_2()
{
for (var i = ; i < ; i++)
{
list.Add(i);
}
}
}

多跑几次这段代码,你几乎可以重现所有可能出现的多线程资源争用异常.
解决方案:给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 = ; i < ; i++)
{
lock (lockObj)
{
list.Add(i);
}
}
} static void Task_1()
{
for (var i = ; i < ; i++)
{
lock (lockObj)
{
list.Add(i);
}
}
}
static void Task_2()
{
for (var i = ; i < ; i++)
{
lock (lockObj)
{
list.Add(i);
}
}
}
}
ok,解决了问题,当然这不是最好的解决方案,你完全可以通过适配器模式,去扩展一个线程安全的List类型,这里我就不写了.
C# 多线程之List的线程安全问题的更多相关文章
- 多线程之:java线程创建
java中创建线程有两种方式: 1.继承Thread类,重写run()方法,如: public class MyThread extends Thread { public void run(){ S ...
- JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止
JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止 背景 当单线程的程序发生一个未捕获的异常时我们可以采用try....catch进行异常的捕获,但是在多线程环境 ...
- iOS多线程之8.NSOPeration的其他用法
本文主要对NSOPeration的一些重点属性和方法做出介绍,以便大家可以更好的使用NSOPeration. 1.添加依赖 - (void)addDependency:(NSOperation * ...
- python 线程之 threading(四)
python 线程之 threading(三) http://www.cnblogs.com/someoneHan/p/6213100.html中对Event做了简单的介绍. 但是如果线程打算一遍一遍 ...
- python 线程之 threading(三)
python 线程之 threading(一)http://www.cnblogs.com/someoneHan/p/6204640.html python 线程之 threading(二)http: ...
- python 线程之_thread
python 线程之_thread _thread module: 基本用法: def child(tid): print("hello from child",tid) _thr ...
- Java多线程之ConcurrentSkipListMap深入分析(转)
Java多线程之ConcurrentSkipListMap深入分析 一.前言 concurrentHashMap与ConcurrentSkipListMap性能测试 在4线程1.6万数据的条件下, ...
- 【C#】线程之Parallel
在一些常见的编程情形中,使用任务也许能提升性能.为了简化变成,静态类System.Threading.Tasks.Parallel封装了这些常见的情形,它内部使用Task对象. Parallel.Fo ...
- (转)java线程安全问题之静态变量、实例变量、局部变量
java多线程编程中,存在很多线程安全问题,至于什么是线程安全呢,给出一个通俗易懂的概念还是蛮难的,如同<java并发编程实践>中所说: 写道 给线程安全下定义比较困难.存在很多种定义,如 ...
随机推荐
- Netty4.x 源码实战系列(一): 深入理解ServerBootstrap 与 Bootstrap
转载自:https://www.cnblogs.com/itdriver/p/8149913.html 从Java1.4开始, Java引入了non-blocking IO,简称NIO.NIO与传统s ...
- MFC模块状态(一)
先看一个例子: 1.创建一个动态链接到MFC DLL的规则DLL,其内部包含一个对话框资源.指定该对话框ID如下: #define IDD_DLL_DIALOG 2000 ...
- 2.2.1synchronized方法的弊端
缺陷:用关键字synchronized声明方法是有弊端的,譬如A线程调用同步方法执行一个长时间的任务,那么B线程则必须等待较长的时间, 解决方法:使用synchronized同步语句块 package ...
- Ubuntu安装spyder并添加快捷方式
Ubuntu安装spyder并添加快捷方式 之前通过 sudo apt install spyder sudo apt install spyder3 安装spyder,但是这样有个坏处,编译器只能用 ...
- java基本数据类型与封装类型详解(int和Integer区别)
int是java提供的8种原始数据类型之一. Java为每个原始类型提供了封装类,Integer是java为int提供的封装类(即Integer是一个java对象,而int只是一个基本数据类型).in ...
- python常用算法题
一: #计算平均数,中位数和众数 #coding:utf-8 #计算平均数,中位数和众数 import random lst = [random.randint(0,10) for i in rang ...
- POJ3111 K Best 2017-05-11 18:12 31人阅读 评论(0) 收藏
K Best Time Limit: 8000MS Memory Limit: 65536K Total Submissions: 10261 Accepted: 2644 Case Time ...
- codeforce868c
C. Qualification Rounds time limit per test 2 seconds memory limit per test 256 megabytes input stan ...
- PHP后台图片上传作品 接口
//把新图片添加到文件夹里 public function info($file=''){ $info = $file->validate(['ext'=>'jpg'])->rule ...
- SRM469
250pt 在一个10^9 * 10^9大的剧院里,有最多47个位子有人,然后有一对couple想找一对左右相邻的位子,问有多少种选择方式. 思路: 总共有 n * (m-1)种方案,然后扣掉有人位置 ...