.NET的StringBuilder是否是线程安全的

时间:2012-01-12 07:15:42

标签: .net thread-safety stringbuilder

StringBuilder的MSDN文档的常规“线程安全”部分指出:

  

...不保证任何实例成员都是线程安全的......

但是这个陈述感觉它已被复制并粘贴到框架中的几乎每个类:

http://msdn.microsoft.com/en-us/library/system.text.stringbuilder.aspx

然而,Gavin Pugh的这些博客文章提到了StringBuilder的线程安全行为:

http://www.gavpugh.com/2010/03/23/xnac-stringbuilder-to-string-with-no-garbage/

http://www.gavpugh.com/2010/04/01/xnac-avoiding-garbage-when-working-with-stringbuilder/

此外,Reflector揭示了StringBuilder的源代码,以及随附的注释  在SSCLI源代码中,还提出了许多实现注意事项以确保线程安全:

http://labs.developerfusion.co.uk/SourceViewer/browse.aspx?assembly=SSCLI&namespace=System.Text&type=StringBuilder

有没有人更了解StringBuilder实例是否可以安全地在多个并发线程之间共享?

3 个答案:

答案 0 :(得分:25)

绝对不是;这是一个从4.0通过反射器提升的简单例子:

[SecuritySafeCritical]
public StringBuilder Append(char value)
{
    if (this.m_ChunkLength < this.m_ChunkChars.Length)
    {
        this.m_ChunkChars[this.m_ChunkLength++] = value;
    }
    else
    {
        this.Append(value, 1);
    }
    return this;
}

该属性只处理调用者,而不是线程安全;这绝对不是线程安全的。

更新:查看他引用的源代码,这显然不是当前的.NET 4.0代码库(比较几种方法)。也许他正在谈论一个特定的.NET版本,或者可能是XNA - 但它通常是的情况。 4.0 StringBuilder没有 一个m_currentThread字段,Gavin的源材料使用该字段;有一个提示(一个未使用的常量ThreadIDField使用存在,但是......不再存在。


如果您想要直接防范 - 请在4.0上运行;它很可能会给出错误的长度(我在4k区域看到了一些,在2k区域看到了一些 - 它应该是5000),但是其他一些Append方法(Append(char)用于例如,取决于时间,更倾向于抛出异常:

var gate = new ManualResetEvent(false);
var allDone = new AutoResetEvent(false);
int counter = 0;
var sb = new StringBuilder();
ThreadStart work = delegate
{
    // open gate when all 5 threads are running
    if (Interlocked.Increment(ref counter) == 5) gate.Set();
    else gate.WaitOne();

    for (int i = 0; i < 1000; i++) sb.Append("a");

    if (Interlocked.Decrement(ref counter) == 0) allDone.Set();
};
for(int i = 0 ; i < 5 ; i++)
{
    new Thread(work).Start();
}
allDone.WaitOne();
Console.WriteLine(sb.Length);

答案 1 :(得分:6)

文档的重点是为您提供保证。在这种情况下,在实例成员上没有任何保证是线程安全的,你应该这样对待它,因此依赖于外部同步方法。

某些可能线程安全的东西是实现细节,它可以并且可能确实从框架的一个版本更改为下一个版本或从一个实现更改为下一个实现(事实上,在框架版本中有很多这样的细节变化; Eric Lippert有一些帖子详细介绍了其中的一些)。不要依赖它。

(换句话说:不要将代码写入实现,将其写入接口和契约,这是该类及其文档的元数据。)

答案 2 :(得分:1)

来自MSDN documentation

  

此类型的任何公共静态(在Visual Basic中为Shared)成员都是   线程安全。任何实例成员都不能保证是线程   安全