【问题标题】:Is .NET's StringBuilder thread-safe.NET 的 StringBuilder 是线程安全的吗
【发布时间】:2012-01-12 07:15:42
【问题描述】:

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的来源,以及附带的cmets 在 SSCLI 源代码中,还建议了许多实现注意事项以确保线程安全:

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

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

【问题讨论】:

    标签: .net thread-safety stringbuilder


    【解决方案1】:

    绝对不是;这是一个通过反射器从 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);
    

    【讨论】:

    【解决方案2】:

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

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

    (换句话说:不要将代码写入实现,而是针对接口和契约编写代码,在这种情况下,它是类的元数据及其文档。)

    【讨论】:

      【解决方案3】:

      来自MSDN documentation

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

      【讨论】:

        猜你喜欢
        • 2015-08-09
        • 2011-08-02
        • 2010-10-12
        • 2013-06-10
        • 2010-09-28
        • 2011-04-16
        • 2021-12-27
        • 1970-01-01
        • 2021-10-12
        相关资源
        最近更新 更多