Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# C&x27';操作员线程安全?_C#_.net_Thread Safety_Null Coalescing Operator - Fatal编程技术网

C# C&x27';操作员线程安全?

C# C&x27';操作员线程安全?,c#,.net,thread-safety,null-coalescing-operator,C#,.net,Thread Safety,Null Coalescing Operator,每个人都知道这不是线程安全的: public StringBuilder Builder { get { if (_builder != null) _builder = new StringBuilder(); return _builder; } } 这个怎么样 public StringBuilder Builder { get { return _builder ?? (_builder = n

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

public StringBuilder Builder
{
    get 
    {
        if (_builder != null)
            _builder = new StringBuilder();
        return _builder; 
    }
}
这个怎么样

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

两个版本的线程安全性都没有;您仍然可以让两个线程同时执行空检查,从而创建单独的对象而看不到另一个。

开始编辑

根据您编辑的标题,空合并操作符本身似乎是线程安全的(请参阅)。但是,它似乎不能保证不会对StringBuilder构造函数进行潜在的多次调用

结束编辑

线程有一个更大的问题,那就是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中的实例方法不是设计为线程安全的。请参阅下面的文本

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

如果您在多个线程中使用StringBuilder,那么最好在类中封装它。将生成器设置为私有,并将所需的行为公开为公共方法:

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

这样你就不会到处写同步代码了。

不,原子也不是。

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

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

using System.Threading;

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

仅当_builder==null时,对CompareExchange()的调用才会使用StringBuilder的新实例对_builder中的值执行原子替换。联锁类上的所有方法都不需要被线程开关抢占。

我只是认为??这是原子操作吗?线程也不安全吗?我不能说空合并操作符是否是原子的,但我的断言是,您有一个更大的问题,因为StringBuilder本质上不是线程安全的。有关空合并操作符的线程安全性的答案,请参阅编辑过的答案(归功于Phil Haack)。它是线程安全的,因为它不会创建争用条件,但是如果条件完美,您可能会得到两个单独的Builder实例。对,它确实引入了争用条件。如果构造了两个StringBuilder,哪一个被分配给
\u builder
?您对使用Interlocated.CompareExchange(ref\u builder,new StringBuilder(),null)有何看法?顺便说一句,共享StringBuilder的实例可能不是一个好主意。SB本质上不是线程安全的,即使它是线程安全的,也不清楚您是否可以跨未同步的线程使用它做任何有意义的事情;空合并运算符不是原子的。空合并运算符只是第一段代码的语法糖。但这里有更大的问题;谁在乎这片土地是否安全?建造者不是线程安全的!对于今后的问题,如果您提供一个措辞谨慎的定义,确切说明“线程安全”对您意味着什么,将会有所帮助。线程安全不是绝对的;相反,如果调用方实现的使用约定与被调用方预期的使用约定兼容,则代码是线程安全的。如果不知道您期望的契约是什么,就无法确定代码是否遵循它。