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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 03:15:40  来源:igfitidea点击:

Is a string property itself threadsafe?

c#multithreading

提问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
         }
    }
}

DoWorkmay well loop forever, despite the write to the boolean variable being atomic - there's nothing to stop the JIT from caching the value of stoppingin DoWork. To fix this, you either need to lock, make the variable volatileor use an explicit memory barrier. This all applies to string properties as well.

DoWork可能会永远循环,尽管写入布尔变量是原子的 - 没有什么可以阻止 JIT 缓存stoppingin的值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 getwould 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.

所以对于它的作用,第一段代码很好。但是,您是否真的想将固有的竞争条件构建到多线程应用程序中是另一回事。