Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.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#_.net_Thread Safety - Fatal编程技术网

C# 这个线安全吗?

C# 这个线安全吗?,c#,.net,thread-safety,C#,.net,Thread Safety,就我所知,以下代码是线程安全的,但需要注意的是,我并不是在寻找单例模式(我不在乎多个线程是否获得不同的MyObject实例,因为MyObject是不可变的。在创建多个实例的并发线程之间发生潜在争用后,后续线程都将获得相同的intance) 以下内容显然不是线程安全的,因为在myObject字段完全初始化之前,多个线程可能会尝试访问该字段: private static MyObject myObject; ... public static MyObject GetMyObject() {

就我所知,以下代码是线程安全的,但需要注意的是,我并不是在寻找单例模式(我不在乎多个线程是否获得不同的MyObject实例,因为MyObject是不可变的。在创建多个实例的并发线程之间发生潜在争用后,后续线程都将获得相同的intance)

以下内容显然不是线程安全的,因为在myObject字段完全初始化之前,多个线程可能会尝试访问该字段:

private static MyObject myObject;
...
public static MyObject GetMyObject()
{
    if (myObject == null)
    {
        myObject = new MyObject();
        myObject.Initialize(...);
    }
    return myObject;
}
以下是线程安全的,还是myObject字段上需要volatile关键字?它看起来是安全的,因为在对象完全初始化之前,myObject字段不会被分配。但我担心抖动可能会内联LoadMyObject方法,并基本上将其重新实现为上面的代码,而这不是线程安全

private static MyObject myObject;
...
private static MyObject LoadMyObject()
{
    MyObject myObject = new MyObject();
    myObject.Initialize(...);
    return myObject;
}

public static MyObject GetMyObject()
{
    if (myObject == null)
    {
        myObject = LoadMyObject();
    }
    return myObject;
}
上面的背景是,我想重构一些多线程代码,并用对工厂方法的调用替换对构造函数的调用。我想知道这样做是否会破坏线程安全

编辑

重申一下,我不在乎是否创建了MyObject的多个实例,只在乎无法访问未完全初始化的实例。作为一个具体的例子,请考虑一个加载了配置文件内容的只读XmlDocument。我希望在内存中提供对XmlDocument的静态引用,以便避免每次访问时从磁盘加载的开销。但我不在乎两个线程是否同时运行,并且都从磁盘加载文档

EDIT2

如果我理解正确,C语言规范中的以下语句似乎暗示抖动不会对代码重新排序,因此我的最后一个示例应该是线程安全的。我理解正确吗

3.10执行令 ... 数据依赖性保存在 执行线程。即 每个变量的值计算为 如果线程中的所有语句都是 按原始程序顺序执行


如果我能为clippy频道一分钟:“看起来你在尝试实现单例模式”。请参阅。

如果我能为clippy频道一分钟:“看起来你在尝试实现单例模式”。请参阅。

为什么不将Initialize方法移动到MyObject的c'tor中,并用一个调用对其进行初始化?

为什么不将Initialize方法移动到MyObject的c'tor中,并用一个调用对其进行初始化?

即使没有编译器优化,它仍然不是线程安全的。Volatile在这种情况下没有帮助,它只是为了保护假设两个线程在完全相同的时间通过空检查-您最终会调用“LoadMyObject”两次,在这种情况下这可能是正常的,也可能不是。这是一个TOCTOU错误(检查时间/使用时间)。您基本上必须使整个GetMyObject方法的主体安全,包括检查(即通过使该调用同步)。

即使没有编译器优化,它仍然不是线程安全的。Volatile在这种情况下没有帮助-它只是为了保护变量不被“隐藏”修改而设计的。假设两个线程在同一时间通过空检查-您将调用“LoadMyObject”两次,在本例中这可能是正常的,也可能不是。这是一个TOCTOU错误(检查时间/使用时间)。您必须确保整个GetMyObject方法的主体安全,包括检查(即使该呼叫同步)。

以下是我的看法:

public class TestClass
{
    private static TestClass instance = LoadObject();

    private TestClass(){ }

    private static TestClass LoadObject()
    {
        var t = new TestClass();
        //do init

        return t;
    }

    public static  TestClass GetObject()
    {
        return instance;
    }
}
以下是我的看法:

public class TestClass
{
    private static TestClass instance = LoadObject();

    private TestClass(){ }

    private static TestClass LoadObject()
    {
        var t = new TestClass();
        //do init

        return t;
    }

    public static  TestClass GetObject()
    {
        return instance;
    }
}

从某种意义上讲,您将获得一个正确初始化的对象是安全的,但是多个线程可能同时创建对象,并且一个线程可能返回一个不同线程刚刚创建的对象

如果编译器内联该方法,它仍然不会与第一个代码相同。由于对对象的引用在初始化之前一直保存在局部变量中,因此仍然是安全的

您可以自己内联该方法,无需将代码放在单独的方法中以确保安全:

public static MyObject GetMyObject() {
   if (myObject == null) {
      MyObject newObject = new MyObject();
      newObject.Initialize(...);
      myObject = newObject;
   }
   return myObject;
}

如果要防止多个对象由单独的线程创建,则需要使用锁。

从安全的意义上讲,可以获得正确初始化的对象,但多个线程可能同时创建对象,并且一个线程可能返回另一个线程刚刚创建的对象

如果编译器内联该方法,它仍然不会与第一个代码相同。由于对对象的引用在初始化之前一直保存在局部变量中,因此仍然是安全的

您可以自己内联该方法,无需将代码放在单独的方法中以确保安全:

public static MyObject GetMyObject() {
   if (myObject == null) {
      MyObject newObject = new MyObject();
      newObject.Initialize(...);
      myObject = newObject;
   }
   return myObject;
}

如果要防止多个对象由单独的线程创建,则需要使用锁。

要回答您的第二次编辑,如果初始化方法需要一段时间,则如果对象不为null,则另一个线程可能会将其视为k,然后将其非初始化状态拉入。我建议您使用锁进行null检查d初始化,以确保仅在完全初始化时检索对象

如果您希望它是完全线程安全的,这将有助于:

private static MyObject myObject;
private object lockObj = new object();
...
public static MyObject GetMyObject()
{
    lock(lockObj)
    {
        if (myObject == null)
        {
            myObject = new MyObject(...);
        }
    }
    return myObject;
}

为了回答您的第二次编辑,如果初始化方法需要一点时间,那么如果对象不为null,则另一个线程可能会将其视为k,然后将其拉入未初始化状态。我建议您对null检查和初始化使用锁,以确保仅在完全初始化时检索对象

如果您希望它是完全线程安全的,这将有助于:

private static MyObject myObject;
private object lockObj = new object();
...
public static MyObject GetMyObject()
{
    lock(lockObj)
    {
        if (myObject == null)
        {
            myObject = new MyObject(...);
        }
    }
    return myObject;
}

在我看来,这似乎过于优化了。与其深入了解编译器正在做的事情的微妙之处,不如选择其中最适合您的一个:

  • 一个实例(单例)
  • 每个线程一个实例(线程静态)