什么是线程安全(C#)?(字符串,数组,......?)

Ben*_*Ben 24 c# arrays string enumeration thread-safety

我对C#很新,所以请耐心等待.我对线程安全感有点困惑.什么时候线程安全什么东西什么时候不安全?

正在阅读从现场总是线程安全的(刚刚从一些被初始化之前读)?

//EXAMPLE
RSACryptoServiceProvider rsa = new RSACrytoServiceProvider();
rsa.FromXmlString(xmlString);  
//Is this thread safe if xml String is predifined 
//and this code can be called from multiple threads?
Run Code Online (Sandbox Code Playgroud)

访问的数组或列表的对象始终是线程安全的(如果你使用一个for循环枚举)?

//EXAMPLE (a is local to thread, array and list are global)
int a = 0;
for(int i=0; i<10; i++)
{
  a += array[i];
  a -= list.ElementAt(i);
}
Run Code Online (Sandbox Code Playgroud)

列举始终/曾经线程安全的?

//EXAMPLE
foreach(Object o in list)
{
   //do something with o
 }
Run Code Online (Sandbox Code Playgroud)

可以写入和读出一个特定的领域不断导致损坏的读取(场的一半被改变,一半仍是不变的)?

感谢您的所有答案和时间.

编辑:我的意思是如果所有线程只读取和使用(不写或更改)对象.(除了最后一个问题,显然我的意思是线程既读又写).因为我不知道普通访问或枚举是否是线程安全的.

Lou*_*nco 26

对于不同的情况,它是不同的,但一般来说,如果所有线程都在读取,则读取是安全的.如果有任何写入,读取或写入都不是安全的,除非它可以原子方式完成(在同步块内或使用原子类型).

不确定读数是否正确 - 你永远不知道幕后发生了什么 - 例如,getter可能需要在第一次使用时初始化数据(因此写入本地字段).

对于字符串,你很幸运 - 它们是不可变的,所以你所能做的就是阅读它们.对于其他类型,您在阅读时必须采取预防措施,防止其他线程发生变化.


Eri*_*ert 13

从字段读取(只读取之前初始化的东西)总是线程安全吗?

C#语言保证在3.10节中读取和写入位于单个线程上时,读取和写入始终是一致的:


数据依赖性保留在执行的线程中.也就是说,计算每个变量的值,就好像线程中的所有语句都以原始程序顺序执行一样.保留初始化排序规则.


多线程,多处理器系统中的事件不一定具有彼此相关的明确定义的一致排序. C#语言不保证一致的排序.当从另一个线程观察时,可以观察到一个线程观察到的写入序列处于完全不同的顺序,只要不涉及关键执行点即可.

因此,这个问题无法回答,因为它包含一个未定义的单词.对于多线程多处理器系统中的事件,您能否准确定义"之前"对您意味着什么?

该语言保证仅在关键执行点方面排序副作用,即使这样,在涉及异常时也不会做出任何强有力的保证.再次引用第3.10节:


执行C#程序,以便在关键执行点保留每个执行线程的副作用.副作用定义为易失性字段的读取或写入,对非易失性变量的写入,对外部资源的写入以及抛出异常.必须保留这些副作用的顺序的关键执行点是对volatile字段,锁定语句以及线程创建和终止的引用.[...]关于易失性读取和写入,保留了副作用的顺序.

此外,执行环境不需要评估表达式的一部分,如果它可以推断出不使用该表达式的值并且不产生所需的副作用(包括由调用方法或访问volatile字段引起的任何副作用).当程序执行被异步事件(例如另一个线程抛出的异常)中断时,不能保证可观察的副作用在原始程序顺序中可见.


从数组或列表访问对象始终是线程安全的(如果您使用for循环进行枚举)?

通过"线程安全",你的意思是从列表中读取时,两个线程总是会观察到一致的结果吗?如上所述,C#语言在从变量读取时对结果的观察提供非常有限的保证.在非易失性读数方面,您能否准确定义"线程安全"对您意味着什么?

枚举总是/永远是线程安全吗?

即使在单线程场景中,在枚举时修改集合也是违法的.在多线程场景中这样做肯定是不安全的.

写入和读取特定字段是否会导致读取损坏(字段的一半已更改,一半仍未更改)?

是.我引用你的第5.5节,其中说明:


以下数据类型的读取和写入是原子的:bool,char,byte,sbyte,short,ushort,uint,int,float和reference类型.此外,在先前列表中具有基础类型的枚举类型的读取和写入也是原子的.其他类型的读写,包括long,ulong,double和decimal,以及用户定义的类型,不保证是原子的.除了为此目的而设计的库函数之外,不保证原子读 - 修改 - 写,例如在递增或递减的情况下.