String类中的线程安全性

use*_*169 18 c# multithreading thread-safety

使用String类如下面的方法从本地变量构建字符串是否安全?假设从多个线程调用下面的方法.

public static string WriteResult(int value, string name)
{
    return string.Format("Result: value={0} name={1}", value, name);
}

public static string WriteResult2(int value, string name)
{
    return "Result: value=" + value + " name=" + name;
}
Run Code Online (Sandbox Code Playgroud)

或者我需要使用StringBuilder以确保线程安全吗?

Jon*_*eet 20

那绝对没问题.除了字符串文字之外,在任何一段代码中都没有共享状态.由于字符串是不可变的,因此在线程之间自由共享字符串是很好的,string.Format并且string.Concat(在第二段代码中隐式调用)都是线程安全的.

即使其中一个参数是可变的,即使该方法突变了参数,例如

public static void AddResult(int value, List<string> results)
{
    results.Add("Value " + value);
}
Run Code Online (Sandbox Code Playgroud)

...然后方法本身仍然是线程安全的,只要多个线程没有引用相同的线程List<string>.如果多个线程确实引用相同的List<string>话,那么即使它只是从列表中读取它也是不安全的,因为另一个线程可能会改变它.


Eug*_*kal 18

此方法中的两个intstring作为参数都是有效不可变的,并且不能通过外部代码更改.

因此,在这种情况下,无需使用Format方法或String.Concat来关心线程安全性.


但是,让我们假设我们有一些可变的类MyObject,可以从外部更改:

public class MyClass
{
    public Int32 value1 { get; set; }
    public String value2 { get; set;}
} 

public static string WriteResult2(MyObject obj)
{
    return "Result: value=" + obj.value1 + " name=" + obj.value2 ;
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,无论是第一种方法还是第二种方法,您都可以返回不一致的值(在将一个值放入输出后,value1和value2都会更改.)

正如@JonSkeet 指出的那样,实际上方法本身并不安全,但是在不同线程之间以这种方式共享类本身是不安全的.

要处理这种情况,您必须创建特殊的线程安全实例方法:

public class MyClass
{
    private Object lockObj = new Object();
    public Int32 value1
    {
        get
        {
            lock (this.lockObj) { ... });
        }
        set
        {
            lock (this.lockObj) { ... });
        }
    }
    public String value2
    {
        get
        {
            lock (this.lockObj) { ... });
        }
        set
        {
            lock (this.lockObj) { ... });
        }
    }

    public string WriteResult2()
    {
        lock (this.lockObj)
        {
             return "Result: value=" + this.value1 + " name=" + this.value2 ;
        }
    }
} 
Run Code Online (Sandbox Code Playgroud)

或者在使用它的方法中对这些实例使用一些额外的锁定.第一个类内方法显然不易出错,但可能会降低性能并创建大量的样板代码.理想情况下,在并发编程中,您需要关注共享可变状态及其一致性越少越好.

  • 我不会说你的后一种情况会使*方法*线程不安全 - 它使得*type*(MyClass)不安全地在多个线程之间共享实例,这与IMO略有不同. (7认同)
  • @JenishRabadiya我们需要确保值`value1`和`value2`必须在`WriteResult`方法中保持一致 - 在构建输出字符串时不能更改值.例如`value1` = Jenish,`value2` = Rabadiya,我们称之为`WriteResult`,它写Jenish,但外部代码在同一时刻将`value2`更改为Podskal.这显然不是我们想要的.它仍然允许我们在`WriteResult`调用之前做出这种不一致的状态,所以我们应该删除两个setter并为这两个属性制作一个sync setter方法.或者使用不可变类型. (2认同)

das*_*ght 5

这两种方法都是线程安全的,因为你在里面做什么并不重要WriteResult.只要它不使用可变静态,并且其参数不能从外部更改,您的静态方法就是线程安全的.

很容易验证方法不使用静态.验证方法的参数不能从外部更改也很容易:

  • value 无法更改,因为它是原始类型,并且它是通过值传递的
  • name无法更改,因为string对象是不可变的.