gpt4 book ai didi

c# - 使用双重检查锁定的内存读取可见性排序与写入顺序

转载 作者:太空狗 更新时间:2023-10-29 21:22:06 26 4
gpt4 key购买 nike

我有以下函数,旨在“内存”无参数函数。意思是只调用函数一次,然后所有其他时间都返回相同的结果。

private static Func<T> Memoize<T>(Func<T> func)
{
var lockObject = new object();
var value = default(T);
var inited = false;

return () => {
if (inited)
return value;

lock (lockObject) {
if (!inited) {
value = func();
inited = true;
}
}

return value;
};
}

我能确定如果线程在锁外读取“inited == true”,那么它会读取在“inited”设置为 true 之前写入的“值”吗?

备注:Double-checked locking in .NET涵盖了它应该工作的事实,这个问题主要是为了检查我的实现是否正确,并可能获得更好的替代方案。

最佳答案

不,因为inited不是 volatile . volatile为您提供建立正确的happens-before 关系所需的内存释放和获取栅栏。

如果inited之前没有释放围栏设置为 true,则 value另一个线程读取时可能尚未完全写入 inited并将其视为 true,这可能会导致返回一个构建了一半的对象。同样,如果在读取inited之前有释放围栏但没有对应的获取围栏在第一次检查中,对象可能已完全构建,但看到 inited 的 CPU 核心as true 还没有看到 value 的内存效应正在写入(缓存一致性不一定要求连续写入的效果在其他内核上按顺序显示)。这将再次可能导致返回半构建的对象。

顺便说一下,这是一个已经被很好记录的双重检查锁定模式的实例。

我建议不要使用捕获局部变量的 lambda(这将使编译器生成一个隐式类来保存非 volatile 字段中的封闭变量),而是显式创建您自己的类 volatile申请 value .

private class Memoized<T>
{
public T value;
public volatile bool inited;
}

private static Func<T> Memoize<T>(Func<T> func)
{
var memoized = new Memoized<T>();

return () => {
if (memoized.inited)
return memoized.value;

lock (memoized) {
if (!memoized.inited) {
memoized.value = func();
memoized.inited = true;
}
}

return memoized.value;
};
}

当然,正如其他人提到的那样Lazy<T>正是为了这个目的而存在。使用它而不是自己滚动,但了解某些事物工作原理背后的理论总是一个好主意。

关于c# - 使用双重检查锁定的内存读取可见性排序与写入顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29901967/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com