问题描述
publicvoidParallelBreak(){Console.WriteLine("n—————{0}—————",MethodBase.GetCurrentMethod().Name);ConcurrentBag<int>bag=newConcurrentBag<int>();stopWatch.Start();Parallel.For(0,1000,(i,state)=>{//此处加lock(bag)则输出一定是300.否则不一定是300,可能是302,300,306等if(bag.Count>=300){state.Stop();return;}bag.Add(i);});stopWatch.Stop();Console.WriteLine("Bagcountis"+bag.Count+","+stopWatch.ElapsedMilliseconds);}
以上代码,输出为:不太明白,既然是线程安全类,为什么不加lock就不能保证输出为300呢?
解决方案
解决方案二:
bag.Count>=300的判断和bag.Add两者不是原子操作。可能两个线程都判断bag.Count=299,然后各自插入一个,虽然读取、插入是线程安全的,但是不能保证结果是300
解决方案三:
引用1楼caozhy的回复:
bag.Count>=300的判断和bag.Add两者不是原子操作。可能两个线程都判断bag.Count=299,然后各自插入一个,虽然读取、插入是线程安全的,但是不能保证结果是300
多谢版主,还提一个问题:下面的这段代码,大多数情况是可以运行的,但有时又会报下图的错误,是什么原因呢?publicstaticvoidListWithParallel(){Console.WriteLine("—————{0}—————",MethodBase.GetCurrentMethod().Name);List<int>list=newList<int>();Parallel.For(0,10000,item=>{list.Add(item);});Console.WriteLine("List'scountis{0}",list.Count());}
解决方案四:
线程安全的意思就是多线程做某个操作结果和你预期的是一样的List不是线程安全的,同时添加数据自然会出问题如果你要刨根问底,为什么会出这个错,不妨看下List.Capcity的代码
解决方案五:
线程安全跟加不加lock没有关系。所谓线程安全,是说它的任何在单线程上安全的操作在瞬间并发时也不会抛出逻辑出错。例如一个线程读取list[2]单元,另一个线程也读取list[2]单元,在逻辑上不会抛出异常!只要一个操作在瞬间并发时并不会出现逻辑错误,这就是线程安全的。再比如说一个线程执行list.Move(n),另一个线程也执行list.Move(n),或许结果是对的(例如删除了某个对象)也可能是错误(例如把紧挨在一起的两个对象都删除了),但是不会报错,这就说明它是安全的。比如说一个线程为list增加了一个单元,并不会影响另外一个线程读取list.Count,这就是说明它是线程安全的。线程安全的,并不代表着“先后次序的不同操作”不需要考虑逻辑出错问题。线程安全的东西,实际上一点也没有保证数据不会混乱!!!你在设计前后流程逻辑时照样要使用lock等等同步考虑,你不知道线程安全是在哪一个具体的点上“是安全的”,怎么就能随便“抠字眼儿”就说线程安全就不用lock了呢?线程安全的意思,是说技术数据混乱了,程序仍然不报错。这就好比如说你使用一个NoSql而可以随便并发修改数据(甚至混乱地修改数据),儿你并不会像传统的关系数据库一样收到“事务冲突、事务加锁超时”之类的异常。实际上,线程安全的,就意味着它很容易数据混乱,而并不会报错!所以仍然需要你亲自编代码用lock等语句来保证业务意义上的数据一致性。
解决方案六:
比如说一个国家它说“随便什么人都可以越境进入,根本不会因为偷渡而被捕”,于是你就说这个国家是“安全的”。请问这个国家的治安是安全的吗?你就是误会了“线程安全的”这个概念。线程安全的,并不意味着你要少一点点对lock等等数据安全的考虑。有些类不但是“线程安全的”,而且花费了巨大的时间和空间代码来做到了“私有化”,你认为的“安全”是那种东西低效率的安全吧?!
解决方案七:
很高兴能解答这个问题。其实您只要记住一句话就好:线程安全和数据同步是两码事儿。您的程序是线程安全的,不会出错(起码计算机是这么认为的)但他的数据是不同步的,因为你是并发运行,速度太快,计算机也不确定谁先谁后。所以你需要同步一下。就是LOCK起来。。C#有很多线程同步的类什么信号量互斥量锁自旋锁。看你需要选择合适的用。