C# 字符串属性本身是线程安全的吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/434890/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is a string property itself threadsafe?
提问by TomTom
String's in C# are immutable and threadsafe. But what when you have a public getter property? Like this:
C# 中的字符串是不可变的和线程安全的。但是,当您拥有公共 getter 属性时怎么办?像这样:
public String SampleProperty{
get;
private set;
}
If we have two threads and the first is calling 'get' and the second is calling 'set' at the "same" time, what will happen?
如果我们有两个线程,第一个线程调用“get”,第二个线程“同时”调用“set”,会发生什么?
IMHO the set must made a lock to be thread-safe like this:
恕我直言,该集合必须像这样锁定线程安全:
private string sampleField;
private object threadSafer = new object();
public String SampleProperty{
get{ return this.sampleField; }
private set{
lock(threadSafer){
sampleField = value;
}
}
}
采纳答案by Jon Skeet
Most of the answers are using the word "atomic" as if atomic changes are all that are needed. They're not, usually.
大多数答案都使用“原子”这个词,好像原子变化就是所需要的。他们不是,通常。
This has been mentioned in the comments, but not usually in the answers - that's the only reason for me providing this answer. (The point about locking at a coarser granularity, to allow things like appending, is entirely valid as well.)
这已在评论中提到,但通常不在答案中-这是我提供此答案的唯一原因。(关于以更粗粒度的锁定,以允许诸如附加之类的事情的观点也是完全有效的。)
Usually you want a reading thread to see the latestvalue of the variable/property. That isn't guaranteed by atomicity. As a quick example, here's a badway to stop a thread:
通常你想要一个阅读线程来查看变量/属性的最新值。这不是由 atomicity 保证的。举个简单的例子,这是一个停止线程的坏方法:
class BackgroundTaskDemo
{
private bool stopping = false;
static void Main()
{
BackgroundTaskDemo demo = new BackgroundTaskDemo();
new Thread(demo.DoWork).Start();
Thread.Sleep(5000);
demo.stopping = true;
}
static void DoWork()
{
while (!stopping)
{
// Do something here
}
}
}
DoWork
may well loop forever, despite the write to the boolean variable being atomic - there's nothing to stop the JIT from caching the value of stopping
in DoWork
. To fix this, you either need to lock, make the variable volatile
or use an explicit memory barrier. This all applies to string properties as well.
DoWork
可能会永远循环,尽管写入布尔变量是原子的 - 没有什么可以阻止 JIT 缓存stopping
in的值DoWork
。要解决此问题,您需要锁定、创建变量volatile
或使用显式内存屏障。这一切也适用于字符串属性。
回答by Marc Gravell
A reference-type field's get/set (ldfld/stfld) is (IIRC) guaranteed to be atomic, so there shouldn't be any risk of corruption here. So it should be thread-safe from thatangle, but personally I'd lock the data at a higher level - i.e.
引用类型字段的 get/set (ldfld/stfld) 保证 (IIRC) 是原子的,因此这里不应该有任何损坏的风险。所以从这个角度来看它应该是线程安全的,但我个人会将数据锁定在更高的级别 - 即
lock(someExternalLock) {
record.Foo = "Bar";
}
or maybe:
或者可能:
lock(record.SyncLock) {
record.Foo = "Bar";
}
This allows you to make multiple reads/updates to the same objectas an atomic operation, so that other threads can't get an invalid object-state
这允许您对同一对象进行多次读取/更新作为原子操作,以便其他线程无法获得无效的对象状态
回答by Binary Worrier
Setting the string is an atomic operation, i.e. you will either get the new string or the old string, you'll never get garbage.
设置字符串是一个原子操作,即你要么得到新字符串,要么得到旧字符串,你永远不会得到垃圾。
If you're doing some work e.g.
如果你正在做一些工作,例如
obj.SampleProperty = "Dear " + firstName + " " + lastName;
then string concatination all happens before the call to set, therefore sampleField will always either be the new string or the old.
然后字符串连接都发生在调用 set 之前,因此 sampleField 将始终是新字符串或旧字符串。
If however your string concatination code is self referential e.g.
但是,如果您的字符串连接代码是自引用的,例如
obj.SampleProperty += obj.SampleProperty + "a";
and else where on another thread you have
否则你在另一个线程上的位置
obj.SampleProperty = "Initial String Value";
Then you need the lock.
然后你需要锁。
Consider you're working with an int. If you're assigning to the int, and any value you get from the int is valid, then you don't need to lock it.
考虑您正在使用 int。如果您分配给 int,并且您从 int 获得的任何值都是有效的,那么您不需要锁定它。
However, if the int is keeping count of the number of widgets processed by two or more threads, for the count to be accurate, you need to lock the int. It's the same situation for strings.
但是,如果 int 保持对由两个或更多线程处理的小部件数量的计数,为了使计数准确,您需要锁定 int。字符串也是同样的情况。
I've a feeling I didn't explain this very well, hope it helps.
我觉得我没有很好地解释这一点,希望它有所帮助。
Thanks
谢谢
BW
体重
回答by Kent Boogaart
This is thread-safe without any need for locking. Strings are reference types, so only a reference to the string is being modified. References are of a type are guaranteed to be atomic (Int32 on 32 bit systems and Int64 on 64 bit).
这是线程安全的,不需要锁定。字符串是引用类型,因此只修改了对字符串的引用。引用的类型保证是原子的(32 位系统上的 Int32 和 64 位系统上的 Int64)。
回答by Greg Beech
Your second code sample is definitely not right, because locks only have the desired effect when they are used in allplaces where the variable is accessed (both for get andset), so the get
would also need a lock.
你的第二个代码示例肯定是不对的,因为只有在访问变量的所有地方(获取和设置)使用锁时,锁才会产生预期的效果,因此get
还需要锁。
However, when getting and setting a reference-type field as a property like this, then adding a lock statement doesn't add any value. Assignments to pointers are guaranteed to be atomic in the .NET environment, and if multiple threads are changing a property then you have an inherent race condition anyway (where threads may see different values; this may or may not be a problem) so there's little point in locking.
但是,当获取和设置引用类型字段作为这样的属性时,添加 lock 语句不会添加任何值。在 .NET 环境中,指针的赋值保证是原子的,如果多个线程正在改变一个属性,那么无论如何你都有一个固有的竞争条件(线程可能会看到不同的值;这可能是也可能不是问题)所以几乎没有指向锁定。
So for what it does, the first piece of code is fine. But whether you really want to build inherent race conditions into a multi-threaded application is another matter.
所以对于它的作用,第一段代码很好。但是,您是否真的想将固有的竞争条件构建到多线程应用程序中是另一回事。