字符串属性本身是线程安全的吗?

时间:2009-01-12 09:23:15

标签: c# multithreading

C#中的字符串是不可变的和线程安全的。但是当你有一个公共吸气剂属性时呢?像这样:

public String SampleProperty{
    get;
    private set;
}

如果我们有两个线程,第一个是调用'get'而第二个是在'相同'时调用'set',那么会发生什么?

恕我直言,该集合必须锁定为线程安全,如下所示:

private string sampleField;
private object threadSafer = new object();

public String SampleProperty{
    get{ return this.sampleField; }
    private set{
        lock(threadSafer){
            sampleField = value;
        }
    }
 }

5 个答案:

答案 0 :(得分:41)

大多数答案都使用“原子”这个词,好像原子变化都是需要的。他们通常不是。

这已在评论中提及,但通常不在答案中 - 这是我提供此答案的唯一原因。 (关于以更粗糙的粒度锁定,允许附加等内容的观点也是完全有效的。)

通常您需要一个阅读线程来查看变量/属性的最新值。那isn't guaranteed by 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可能永远循环,尽管写入布尔变量是原子的 - 没有什么可以阻止JIT缓存stoppingDoWork的值。要解决此问题,您需要锁定,生成变量volatile或使用显式内存屏障。这也适用于字符串属性。

答案 1 :(得分:17)

引用类型字段的get / set(ldfld / stfld)是(IIRC)保证是原子的,所以这里不应该有任何损坏的风险。因此,从那个角度来看,它应该是线程安全的,但我个人会将数据锁定在更高的级别 - 即

lock(someExternalLock) {
    record.Foo = "Bar";
}

或者也许:

lock(record.SyncLock) {
    record.Foo = "Bar";
}

这允许您对同一对象进行多次读取/更新作为原子操作,以便其他线程无法获得无效的对象状态

答案 2 :(得分:4)

设置字符串是一个原子操作,即你将获得新字符串或旧字符串,你永远不会得到垃圾。

如果你正在做一些工作,例如

obj.SampleProperty = "Dear " + firstName + " " + lastName;

然后字符串连接都在调用set之前发生,因此sampleField将始终是新字符串或旧字符串。

但是,如果您的字符串连接代码是自引用的,例如

obj.SampleProperty += obj.SampleProperty + "a";

以及你在另一个主题上的位置

obj.SampleProperty = "Initial String Value";

然后你需要锁。

考虑你正在使用int。如果你要分配int,并且你从int获得的任何值都是有效的,那么你不需要锁定它。

但是,如果int保持由两个或多个线程处理的小部件数量的计数,为了使计数准确,您需要锁定int。 字符串也是如此。

我有一种感觉,我没有很好地解释这一点,希望它有所帮助。

由于

BW

答案 3 :(得分:0)

这是线程安全的,无需锁定。字符串是引用类型,因此仅修改对字符串的引用。参考类型保证是原子的(32位系统上的Int32和64位上的Int64)。

答案 4 :(得分:0)

您的第二个代码示例肯定是不对的,因为在访问变量的所有位置使用锁时,锁只会产生所需的效果(对于get 设置),所以get也需要锁定。

但是,当获取并将引用类型字段设置为这样的属性时,添加锁定语句不会添加任何值。指针的赋值在.NET环境中保证是原子的,如果多个线程正在更改属性,那么无论如何你都有一个固有的竞争条件(线程可能看到不同的值;这可能是也可能不是问题)所以几乎没有指向锁定。

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