对象分配线程安全吗?

发布于 2024-12-08 16:16:08 字数 583 浏览 0 评论 0原文

这个线程安全吗?具体来说,GetMyObject() 方法是否有可能返回 null?我知道两个线程有​​可能获得 MyObject 的不同实例,但我不关心这一点。 我只是想确保可以安全地假设 GetMyObject() 永远不会返回 null。

   class Foo {
        private static MyObject obj;
        public static MyObject GetMyObject() {
            MyObject o = obj;
            if(o == null) {
                o = new MyObject();
                obj = o;
            }
            return o;
        }
        public static void ClearMyObject() {
            obj = null;
        }
    }

    class MyObject {}

Is this thread safe? Specifically, is it possible for the GetMyObject() method to return null? I understand it is possible for two threads to get a different instance of MyObject but I don't care about that. I just want to make sure it is safe to assume GetMyObject() will never return null.

   class Foo {
        private static MyObject obj;
        public static MyObject GetMyObject() {
            MyObject o = obj;
            if(o == null) {
                o = new MyObject();
                obj = o;
            }
            return o;
        }
        public static void ClearMyObject() {
            obj = null;
        }
    }

    class MyObject {}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

勿忘初心 2024-12-15 16:16:08

这个线程安全吗?

不。

GetMyObject() 方法是否有可能返回 null?

不会。

该方法保证永远不会返回 null。并且所有的读取和写入都保证是原子的。但是,不能保证线程读取静态字段 obj 的最新版本,并且不能保证线程对 obj 的更改顺序具有一致的视图。任意多个线程可能会竞争并观察到 ​​obj 的不同值。我不会认为这段代码是“线程安全”的,但也许你对“线程安全”有不同的定义。这就是问这个问题的问题所在;对于这个术语,没有一个大家都认可的标准定义。

Is this thread safe?

No.

is it possible for the GetMyObject() method to return null?

No.

The method is guaranteed to never return null. And all the reads and writes are guaranteed to be atomic. However, threads are not guaranteed to read the latest version of static field obj, and threads are not guaranteed to have a consistent view of the sequence of changes to obj. Arbitrarily many threads may race and observe different values of obj. I would not consider this code "thread safe" as a result, but maybe you have a different definition of "thread safe". That's the problem with asking this question; there's no standard definition of the term that everyone reliably agrees upon.

海之角 2024-12-15 16:16:08

GetMyObject() 永远不能返回 null。看到这一点的简单方法是注意“o”是一个局部变量,因此没有其他人可以影响它。

GetMyObject() can never return null. The easy way to see this is to note that 'o' is a local variable so no-one else can affect it.

背叛残局 2024-12-15 16:16:08

好吧,让我们来推理一下:

public static MyObject GetMyObject() {
        MyObject o = obj;
        if(o == null) {
            o = new MyObject();
            obj = o;
        }
        return o;

}

只有一个 return 语句。此方法可以生成 null 返回值的唯一方法是,如果唯一的 return 语句 return o 具有 o == null< /code> 执行时为 true

如果执行 return oonull,则意味着我们使用 if 块中出来>onull。我们可以从 o 块中将 o 作为 null 出来的唯一方法是,如果 o == nulltrue 当测试 if 块的条件时(如果 o == nullfalse,则 o != null 为 true,因为 o 是局部变量它不会受到任何其他线程的影响,但是 o == nulltrue 意味着我们最终进入了 if 块,现在当构造函数调用 o = new MyObject() 返回,我们保证 o 不为 null if 中的第二条语句。 块,obj = o,不影响 的值o 再次强调,由于 o 是一个局部变量,因此是否有多个线程烧毁此代码路径并不重要:每个线程都有自己的 o 并且没有其他线程可以触及任何其他线程的 o

因此,无论 o == nulltrue 还是 。 >false,我们最终得到 o == nullif 块完成时,false

因此,该方法保证返回非空值。

我只是想确保假设 GetMyObject() 永远不会返回 null 是安全的。

好吧,如果这就是您关心的一切,那很好。但让我们澄清一件事。您的方法不是线程安全的。完全有可能构造两个 MyObject 实例,并且两个不同的调用者最终可能会看到不同的返回值,即使很明显您的意图是只拥有一个。要解决此问题,我建议仅使用 Lazy

private static Lazy<MyObject> obj;

static Foo() {
    obj = new Lazy<MyObject>(
        () => new MyObject(),
        true
    );
}

public static MyObject GetMyObject() {
    return obj.Value;
}

public static void ClearMyObject() {
    obj = new Lazy<MyObject>(
        () => new MyObject(), 
        true
    );
}

Well, let's reason through this:

public static MyObject GetMyObject() {
        MyObject o = obj;
        if(o == null) {
            o = new MyObject();
            obj = o;
        }
        return o;

}

There is only one return statement. The only way that this method can produce a null return value is if that sole return statement return o has o == null being true when it is executed.

If o is null when return o is executed, that means we came out of the if block with o as null. The only way we can come out of the o block with o as null is if o == null was true when the conditional for the if block was tested (if o == null is false, then o != null is true and since o is a local variable it can not be affected by any other thread. But then o == null being true implies we end up inside the if block and now when the constructor invocation o = new MyObject() returns, we are guaranteed that o is not null. The second statement in the if block, obj = o, does not affect the value of o. Again, since o is a local variable, it doesn't matter if there are multiple threads burning through this code path: each thread has its own o and no other thread can touch any other thread's o.

Therefore, whether or not o == null is true or false, we end up with o == null being false when the if block completes.

Therefore, this method is guaranteed to return a non-null value.

I just want to make sure it is safe to assume GetMyObject() will never return null.

Well, that's fine if that's all you care about. But let's be clear about something. Your method is NOT thread safe. It is perfectly possible for two instances of MyObject to be constructed and two different callers could end up seeing different return values even though it is clear that you intention is to only have one. To fix this, I recommend just using Lazy<T>:

private static Lazy<MyObject> obj;

static Foo() {
    obj = new Lazy<MyObject>(
        () => new MyObject(),
        true
    );
}

public static MyObject GetMyObject() {
    return obj.Value;
}

public static void ClearMyObject() {
    obj = new Lazy<MyObject>(
        () => new MyObject(), 
        true
    );
}
大海や 2024-12-15 16:16:08

它不会返回 null,但根据大多数接受的定义,它绝不是线程安全的。据推测,您希望将对象存储为共享状态并让其他线程访问该对象。在这种情况下,其他线程可能会创建自己的副本(如您所说)并尝试存储它们,但不能保证所有线程都能看到该对象的最新版本(或该对象的任何其他线程版本)。同样,您的 ClearMyObject() 方法也不会执行您认为会执行的操作。

使用 Lazy 代替,它将提供您正在寻找的内容。

public static readonly Lazy; myObject = new Lazy(() => new MyObject(), true);

It won't return null, but it is by no means thread-safe by most accepted definitions. Presumably, you want to store your object into shared state and have that accessed by other threads. In this case, other threads may create their own copies (as you said) and try to store them, but all threads are not guaranteed to see the latest version of that object (or any other thread's version of that object). Likewise, your ClearMyObject() method will not do what you think it will.

Use Lazy<T> instead which will provide what you're looking for.

public static readonly Lazy<MyObject> myObject = new Lazy<MyObject>(() => new MyObject(), true);

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文