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

发布于 2024-11-03 14:20:59 字数 918 浏览 0 评论 0原文

我对 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?

从数组或列表中访问对象始终是线程安全的(如果您使用 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);
}

枚举总是线程安全的吗?

//EXAMPLE
foreach(Object o in list)
{
   //do something with o
 }

对特定字段写入和读取是否会导致读取损坏(该字段的一半已更改,一半仍保持不变)?

感谢您的所有回答和时间。

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

I'm quite new to C# so please bear with me. I'm a bit confused with the thread safety. When is something thread safe and when something isn't?

Is reading (just reading from something that was initialized before) from a field always thread safe?

//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?

Is accessing an object from an array or list always thread safe (in case you use a for loop for enumeration)?

//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);
}

Is enumeration always/ever thread safe?

//EXAMPLE
foreach(Object o in list)
{
   //do something with o
 }

Can writing and reading to a particular field ever result in a corrupted read (half of the field is changed and half is still unchanged) ?

Thank you for all your answers and time.

EDIT: I meant if all threads are only reading & using (not writing or changing) object. (except for the last question where it is obvious that I meant if threads both read and write). Because I do not know if plain access or enumeration is thread safe.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

野生奥特曼 2024-11-10 14:20:59

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

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

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

It's different for different cases, but in general, reading is safe if all threads are reading. If any are writing, neither reading or writing is safe unless it can be done atomically (inside a synchronized block or with an atomic type).

It isn't definite that reading is ok -- you never know what is happening under the hoods -- for example, a getter might need to initialize data on first usage (therefore writing to local fields).

For Strings, you are in luck -- they are immutable, so all you can do is read them. With other types, you will have to take precautions against them changing in other threads while you are reading them.

辞慾 2024-11-10 14:20:59

从字段中读取(仅读取之前初始化的内容)是否始终是线程安全的?

在3.10节中,当读写在单个线程上时,C#语言保证读写顺序一致:


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


多线程、多处理器系统中的事件不一定具有明确定义的相互一致的时间顺序。C# 语言不保证存在一致的顺序。只要不涉及关键执行点,一个线程观察到的一系列写入可能会与另一个线程观察到的顺序完全不同。

因此,该问题无法回答,因为它包含未定义的单词。对于多线程、多处理器系统中的事件,您能否给出“之前”对您意味着什么的精确定义?

该语言保证副作用仅针对关键执行点进行排序,即使如此,在涉及异常时也不会做出任何强有力的保证。再次引用第 3.10 节:


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

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


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

“线程安全”是指两个线程在从列表中读取时始终会观察到一致的结果吗?如上所述,C# 语言对读取变量时观察结果的保证非常有限。对于非易失性读取,您能否给出“线程安全”对您意味着什么的精确定义?

枚举总是线程安全的吗?

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

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

是的。我建议您参考第 5.5 节,其中规定:


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


Is reading (just reading from something that was initialized before) from a field always thread safe?

The C# language guarantees that reads and writes are consistently ordered when the reads and writes are on a single thread in section 3.10:


Data dependence is preserved within a thread of execution. That is, the value of each variable is computed as if all statements in the thread were executed in original program order. Initialization ordering rules are preserved.


Events in a multithreaded, multiprocessor system do not necessarily have a well-defined consistent ordering in time with respect to each other. The C# language does not guarantee there to be a consistent ordering. A sequence of writes observed by one thread may be observed to be in a completely different order when observed from another thread, so long as no critical execution point is involved.

The question is therefore unanswerable because it contains an undefined word. Can you give a precise definition of what "before" means to you with respect to events in a multithreaded, multiprocessor system?

The language guarantees that side effects are ordered only with respect to critical execution points, and even then, does not make any strong guarantees when exceptions are involved. Again, to quote from section 3.10:


Execution of a C# program proceeds such that the side effects of each executing thread are preserved at critical execution points. A side effect is defined as a read or write of a volatile field, a write to a non-volatile variable, a write to an external resource, and the throwing of an exception. The critical execution points at which the order of these side effects must be preserved are references to volatile fields, lock statements, and thread creation and termination. [...] The ordering of side effects is preserved with respect to volatile reads and writes.

Additionally, the execution environment need not evaluate part of an expression if it can deduce that that expression’s value is not used and that no needed side effects are produced (including any caused by calling a method or accessing a volatile field). When program execution is interrupted by an asynchronous event (such as an exception thrown by another thread), it is not guaranteed that the observable side effects are visible in the original program order.


Is accessing an object from an array or list always thread safe (in case you use a for loop for enumeration)?

By "thread safe" do you mean that two threads will always observe consistent results when reading from a list? As noted above, the C# language makes very limited guarantees about observation of results when reading from variables. Can you give a precise definition of what "thread safe" means to you with respect to non-volatile reading?

Is enumeration always/ever thread safe?

Even in single threaded scenarios it is illegal to modify a collection while enumerating it. It is certainly unsafe to do so in multithreaded scenarios.

Can writing and reading to a particular field ever result in a corrupted read (half of the field is changed and half is still unchanged) ?

Yes. I refer you to section 5.5, which states:


Reads and writes of the following data types are atomic: bool, char, byte, sbyte, short, ushort, uint, int, float, and reference types. In addition, reads and writes of enum types with an underlying type in the previous list are also atomic. Reads and writes of other types, including long, ulong, double, and decimal, as well as user-defined types, are not guaranteed to be atomic. Aside from the library functions designed for that purpose, there is no guarantee of atomic read-modify-write, such as in the case of increment or decrement.


如日中天 2024-11-10 14:20:59

好吧,我通常认为一切都是线程不安全的。为了在线程环境中快速而脏地访问全局对象,我使用lock(object)关键字。 .Net 有一套广泛的同步方法,例如不同的信号量等。

Well, I generally assume everything is thread unsafe. For quick and dirty access to global objects in an threaded environment I use the lock(object) keyword. .Net have an extensive set of synchronization methods like different semaphores and such.

莫多说 2024-11-10 14:20:59

如果有任何线程正在写入,则读取可能是线程不安全的(例如,如果它们在读取过程中写入,则会遇到异常)。

如果您必须这样做,那么您可以这样做:

lock(i){
    i.GetElementAt(a)
}

这将强制 i 上的线程安全(只要其他线程在使用 i 之前类似地尝试锁定它。一次只有一件事可以锁定引用类型。

就以下而言)枚举,我会参考MSDN:

The enumerator does not have exclusive access to the collection; therefore, enumerating 
through a collection is intrinsically not a thread-safe procedure. To guarantee thread 
safety during enumeration, you can lock the collection during the entire enumeration. To 
allow the collection to be accessed by multiple threads for reading and writing, you must     
implement your own synchronization.

Reading can be thread-unsafe if there are any threads that are writing (if they write in the middle of a read, for example, they'll be hit with an exception).

If you must do this, then you can do:

lock(i){
    i.GetElementAt(a)
}

This will force thread-safety on i (as long as other threads similarly attempt to lock i before they use it. only one thing can lock a reference type at a time.

In terms of enumeration, I'll refer to the MSDN:

The enumerator does not have exclusive access to the collection; therefore, enumerating 
through a collection is intrinsically not a thread-safe procedure. To guarantee thread 
safety during enumeration, you can lock the collection during the entire enumeration. To 
allow the collection to be accessed by multiple threads for reading and writing, you must     
implement your own synchronization.
零度℉ 2024-11-10 14:20:59

无线程安全的示例:当多个线程递增一个整数时。您可以按照预定的增量数进行设置。但您可能会观察到,int 的增量并没有您想象的那么多。所发生的情况是两个线程可能会增加相同的整数值。这只是您在使用多个线程时可能观察到的过多效果的一个示例。

PS

线程安全增量可通过 Interlocked.Increment(ref i) 获得

An example of no thread-safety: When several threads increment an integer. You can set it up in a way that you have a predeterminded number of increments. What youmay observe though, is, that the int has not been incremented as much as you thought it would. What happens is that two threads may increment the same value of the integer.This is but an example of aplethora of effects you may observe when working with several threads.

PS

A thread-safe increment is available through Interlocked.Increment(ref i)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文