String类中的线程安全性

使用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; } 

或者我是否需要使用StringBuilder来确保线程安全?

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

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

 public static void AddResult(int value, List results) { results.Add("Value " + value); } 

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

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

在这种情况下,无论是第一种方法还是第二种方法,您都可以返回不一致的值(在将一个值放入输出后,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 ; } } } 

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

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

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

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