使用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
来确保线程安全?
答案 0 :(得分:19)
那绝对没问题。除了字符串文字之外,在任何一段代码中都没有共享状态。由于字符串是不可变的,因此在线程之间自由共享字符串是很好的,string.Format
和string.Concat
(在第二段代码中隐式调用)都是线程安全的。
即使其中一个参数是可变的,即使该方法突变了参数,例如
public static void AddResult(int value, List<string> results)
{
results.Add("Value " + value);
}
...然后方法本身仍然是线程安全的,只要多个线程没有引用相同的List<string>
。如果多个线程 引用相同的List<string>
,那么即使它只是从列表中读取它也是不安全的,因为另一个线程可能会改变它。 / p>
答案 1 :(得分:17)
此方法中的int
和string
作为参数实际上是不可变的,并且不能由外部代码更改。
因此,在这种情况下,无需使用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 pointed实际上,方法本身并不是不安全的,但是在不同的线程之间以这种方式共享类本身是不安全的。
要处理这种情况,您必须创建特殊的线程安全实例方法:
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 ;
}
}
}
或者在使用它的方法中对这些实例使用一些额外的锁定。第一个类内方法显然不易出错,但可能会降低性能并创建大量的样板代码。理想情况下,在并发编程中,您需要关注的是共享可变状态及其一致性the better。
答案 2 :(得分:5)
这两种方法都是线程安全的,因为你在WriteResult
内做了什么并不重要。只要它不使用可变静态,并且它的参数不能从外部改变,你的静态方法就是线程安全的。
很容易验证方法不使用静态。还可以轻松验证方法的参数不能从外部更改:
value
无法更改,因为它是原始类型,并且按值传递name
无法更改,因为string
个对象是不可变的。