Kam*_*eri 20 c# multithreading atomic thread-safety
我想知道Linq扩展方法是否是原子的?或者,在进行任何迭代之前,我是否需要跨线程使用的lock任何IEnumerable对象?
声明变量volatile对此有何影响?
总结一下,以下哪项是最好的,线程安全,操作?
1-没有任何锁:
IEnumerable<T> _objs = //...
var foo = _objs.FirstOrDefault(t => // some condition
Run Code Online (Sandbox Code Playgroud)
2-包括锁定语句:
IEnumerable<T> _objs = //...
lock(_objs)
{
var foo = _objs.FirstOrDefault(t => // some condition
}
Run Code Online (Sandbox Code Playgroud)
3-将变量声明为volatile:
volatile IEnumerable<T> _objs = //...
var foo = _objs.FirstOrDefault(t => // some condition
Run Code Online (Sandbox Code Playgroud)
Kri*_*ten 21
该接口IEnumerable<T>不是线程安全的.请参阅http://msdn.microsoft.com/en-us/library/s793z9y2.aspx上的文档,其中说明:
只要集合保持不变,枚举器仍然有效.如果对集合进行了更改(例如添加,修改或删除元素),则枚举数将无法恢复,并且其行为未定义.
枚举器没有对集合的独占访问权限; 因此,枚举通过集合本质上不是一个线程安全的过程.为了在枚举期间保证线程安全,您可以在整个枚举期间锁定集合.要允许多个线程访问集合以进行读写,您必须实现自己的同步.
Linq没有改变任何这一点.
显然,锁定可用于同步对象的访问.您必须在访问它的任何地方锁定对象,而不仅仅是在迭代它时.
将集合声明为volatile将不会产生任何积极影响.它只会在读取之前和写入对集合的引用之后产生内存屏障.它不会同步集合读取或写入.
简而言之,如上所述,它们不是线程安全的.
但是,这并不意味着您必须在"每种迭代"之前锁定.
您需要将更改集合(添加,修改或删除元素)的所有操作与(添加,修改,删除元素或读取元素)的其他操作同步.
如果您只是同时对集合执行读取操作,则不需要锁定.(所以运行像Average,Contains,ElementAtOrDefault这样的LINQ命令就可以了)
如果集合中的元素具有机器字长,例如大多数32位计算机上的Int,则更改该元素的值已经原子化地执行.在这种情况下,不要在没有锁定的情况下添加或删除集合中的元素,但如果您可以在设计中处理某些非确定性,则修改值可能没问题.
最后,您可以考虑对集合的各个元素或部分进行细粒度锁定,而不是锁定整个集合.