8

每个人都知道这不是线程安全的:

public StringBuilder Builder
{
    get 
    {
        if (_builder != null)
            _builder = new StringBuilder();
        return _builder; 
    }
}

那这个呢?

public StringBuilder Builder
{
    get { return _builder ?? (_builder = new StringBuilder()); }
}
4

6 回答 6

10

那或多或少是线程安全的。您仍然可以让两个线程同时进行空值检查,从而创建单独的对象而看不到另一个。

于 2009-06-03T13:19:19.873 回答
10

开始编辑

根据您编辑的标题,空合并运算符本身似乎是线程安全的(请参阅Phil Haack 的分析)。然而,它似乎并不能保证对 StringBuilder 构造函数的潜在多次调用。

结束编辑

线程有一个更大的问题,那就是 Builder 属性本身表示可以跨线程共享的状态。即使您使惰性初始化线程安全,也不能保证使用 Builder 的方法以线程安全的方式进行。

// below code makes the getter thread safe
private object builderConstructionSynch = new object();
public StringBuilder Builder
{
    get
    {
        lock (builderConstructionSynch)
        {
            if (_builder == null) _builder = new StringBuilder();
        }
        return _builder;
    }
}

以上将防止 _builder 的延迟初始化中的线程问题,但除非您将调用同步到 StringBuilder 的实例方法,否则在任何使用 Builder 属性的方法中都不能保证线程安全。这是因为 StringBuilder 中的实例方法并非设计为线程安全的。请参阅MSDN StringBuilder 页面中的以下文本。

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

如果您在多个线程中使用 StringBuilder,则最好将其封装在您的类中。将 Builder 设为私有并将您需要的行为公开为公共方法:

public void AppendString(string toAppend)
{
    lock (Builder)
    {
        Builder.Append(toAppend);
    }
}

这样,您就不会到处编写同步代码。

于 2009-06-03T13:19:29.703 回答
8

两个版本都没有

于 2009-06-03T13:18:53.073 回答
2

不,也不是原子的

于 2009-06-03T13:19:43.447 回答
2

给出的答案是正确的,两者都不是线程安全的。事实上,它们大多是等价的,??操作符只是让代码更精简的编译器魔法。如果你想让它成为线程安全的,你需要使用一些同步机制。

于 2009-06-03T13:26:53.697 回答
2

我自己没有测试过这种方法,但是如果你想要线程安全而不需要锁定方案的开销,并且你不担心可能创建和丢弃对象实例,你可以试试这个:

using System.Threading;

public StringBuilder Builder
{
    get 
    {
        if (_builder != null)
            Interlocked.CompareExchange( ref _builder, new StringBuilder(), null );
        return _builder; 
    }
}

仅当 _builder == null 时,对 CompareExchange() 的调用才会将 _builder 中的值原子替换为 StringBuilder 的新实例。Interlocked 类的所有方法都保证不会被线程切换抢占。

于 2009-06-03T13:39:24.123 回答