我有不可变的对象,我希望延迟计算其哈希码。我已经实施了
private bool _HasHashCode = false;
private int _HashCode;
public override int GetHashCode()
{
if (_HasHashCode)
return _HashCode;
long hashCode;
unchecked
{
hashCode = Digits;
hashCode = (hashCode*397) ^ XI;
hashCode = (hashCode*397) ^ YI;
hashCode = (int) ( hashCode % Int32.MaxValue);
}
// is it possible that these two write instructions
// get reordered on a certain .NET/CPU architecture
// combination:
_HashCode = (int)hashCode;
_HasHashCode = true;
return _HashCode;
}
Run Code Online (Sandbox Code Playgroud)
我的推理是,32 位 _HashCode 成员是 32 位,写入它是原子的,因此即使由于设置 _HasHashCode 属性时的竞争条件而运行两次计算也没关系 …
具体来说,以下之间是否有任何有效的区别:
i = a.load(memory_order_acquire);
Run Code Online (Sandbox Code Playgroud)
或者
a.store(5, memory_order_release);
Run Code Online (Sandbox Code Playgroud)
和
atomic_thread_fence(memory_order_acquire);
i = a.load(memory_order_relaxed);
Run Code Online (Sandbox Code Playgroud)
或者
a.store(5, memory_order_relaxed);
atomic_thread_fence(memory_order_release);
Run Code Online (Sandbox Code Playgroud)
分别?
非宽松原子访问是否提供信号栅栏和线程栅栏?
这是最初的问题,但我的问题与它有一些不同。C++ 内存模型 - 此示例是否包含数据争用?
我的问题:
//CODE-1: initially, x == 0 and y == 0
if (x) y++; // pthread 1
if (y) x++; // pthread 2
Run Code Online (Sandbox Code Playgroud)
注意:上面的代码是用 C 编写的,而不是 C++(没有内存模型)。那么它是否包含数据竞争?
从我的角度来看:如果我们在顺序一致性内存模型中查看代码,则不存在数据竞争,因为 x 和 y 永远不会同时为非零。但是,我们永远不能假设顺序一致性内存模型,因此编译器重新排序可以进行尊重线程内正确性的转换,因为编译器不知道线程的存在......对吗?
所以代码可以改写为:
//CODE-2
y++; if (!x) y--;
x++; if (!y) x--;
Run Code Online (Sandbox Code Playgroud)
上面的转换没有违反顺序正确性,所以它是正确的。这不是编译器的错,对吧?所以我同意 CODE-1 包含数据竞争的观点。你呢?
我有一个额外的问题,带有内存模型的 C++11 可以解决这个数据竞争,因为编译器知道线程,所以他们会根据内存模型类型进行重新排序,对吧?
我正在尝试使用 Turbo C 编译器和链接器编译一个简单的程序并将其链接到 DOS .com 文件。我尝试了我能想到的最简单的 C 程序。
void main()
{}
Run Code Online (Sandbox Code Playgroud)
Turbo C 链接器中是否有链接到 com 文件的命令行参数?
我从链接器收到的错误消息如下:
“致命:无法生成 COM 文件:入口点地址无效”
我知道 com 文件需要入口点位于 100h。Turbo C 是否有设置此地址的选项?
刚刚学完系统的编程讲座材料后,我偶然发现了内存模型以及缓存一致性协议的关键概念。尽管它们作为独立概念是有意义的,但尚不清楚它们如何结合在一起。具体来说,在查看 x86 时,我正在使用强制执行 TSO 内存模型的 ISA,以及使用 MESIF 缓存一致性协议的 CPU(对于 Intel)。
\n\n一开始,教授引入了缓存一致性协议,作为确保芯片中任何核心都访问一个大的单片内存块的手段。然后,在结束了缓存一致性之后,他继续研究内存模型,特别是 TSO(我们已经在并行编程课程中介绍了线性化/顺序一致性)。以下是直接引用有关 x86 内存模型的讲座材料:
\n\n\n\n\n\n
\n- 64 位 x86 处理器的标准\n \n
\n\n
- 有时称为总商店订购 (TSO)
\n- 早期的 32 位 x86 实现的 PRAM \xe2\x80\x93 较弱!
\n- 写入读取放宽:稍后的读取可以绕过早期的写入\n \n
\n\n
- 所有处理器都按照发出的顺序查看来自一个处理器的写入。
\n- 处理器可以看到来自不同处理器的不同写入交错。
\n
似乎我们通过在缓存层次结构中引入(又一个)层,即(有序的)存储缓冲区,“解决”了缓慢的顺序一致性问题。\n对我来说,TSO 似乎与缓存一致性的原则正交。我们非常努力地让我们的缓存匹配,只是在两者之间添加了未被缓存一致性覆盖的另一层。
\n\n问题:
\n\n提前非常感谢您的澄清!
\n\n最好,\n菲利克斯
\n关于std::atomic,C++11 标准规定,存储到原子变量将在“合理的时间内”对该变量的加载变得可见。
从 29.3p13 开始:
\n\n\n\n\n实现应该使原子存储在合理的时间内对原子加载可见。
\n
然而,我很想知道在处理基于 MESI 缓存一致性协议(x86、x86-64、ARM 等)的特定 CPU 架构时实际会发生什么。
\n\n如果我对 MESI 协议的理解是正确的,那么一个核心总是会立即读取先前写入/正在由另一个核心写入的值,可能是通过窥探它。(因为写入值意味着发出 RFO 请求,这反过来会使其他缓存行无效)
\n\n这是否意味着当一个线程 A 将一个值存储到 an 中时std::atomic,另一个连续对该原子进行加载的线程 B 实际上总是会观察到 A 在 MESI 架构上写入的新值?(假设没有其他线程正在对该原子执行操作)
\xe2\x80\x9csuccessively\xe2\x80\x9d 我的意思是在线程 A 发出原子存储之后。(修改顺序已更新)
\n来自链接: 加载/存储宽松原子变量和普通变量有什么区别?
这个回答给我留下了深刻的印象:
使用原子变量解决了这个问题——通过使用原子,即使内存顺序放宽,所有线程都可以保证读取最新的写入值。
今天,我阅读了以下链接:https : //preshing.com/20140709/the-purpose-of-memory_order_consume-in-cpp11/
atomic<int*> Guard(nullptr);
int Payload = 0;
Run Code Online (Sandbox Code Playgroud)
线程1:
Payload = 42;
Guard.store(&Payload, memory_order_release);
Run Code Online (Sandbox Code Playgroud)
线程2:
g = Guard.load(memory_order_consume);
if (g != nullptr)
p = *g;
Run Code Online (Sandbox Code Playgroud)
问题: 我了解到数据依赖会阻止相关指令被重新排序。但我认为这对于确保执行结果的正确性是显而易见的。comsume-release 语义是否存在并不重要。所以我想知道 comsum-release 真的可以。哦,也许它使用数据依赖性来防止指令重新排序,同时确保 Payload 的可见性?
所以
如果我使 1.preventing 指令重新排序 2.确保 Payload 的非原子变量的可见性,是否有可能使用 memory_order_relaxed 获得相同的正确结果:
atomic<int*> Guard(nullptr);
volatile int Payload = 0; // 1.Payload is volatile now
// 2.Payload.assign and Guard.store in order for data dependency
Payload = 42;
Guard.store(&Payload, memory_order_release);
// 3.data Dependency …Run Code Online (Sandbox Code Playgroud) 这让我很困惑,我正在阅读 golang 内存模型,https://golang.org/ref/mem
var l sync.Mutex
var a string
func f() {
a = "hello, world"
l.Unlock()
}
func main() {
l.Lock()
go f()
l.Lock()
print(a)
}
Run Code Online (Sandbox Code Playgroud)
互斥锁通过原子解锁
UnLock: new := atomic.AddInt32(&m.state, -mutexLocked)
Lock: atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked)
Run Code Online (Sandbox Code Playgroud)
我的问题是,原子 AddInt32、CompareAndSwapInt32 是否会导致内存障碍,如果a在不同的 goroutine 中可见的话。
在java中,我知道AtomicInteger,内存屏障通过“易失性”,保持线程字段可见。
当今许多编程语言都具有happens-before关系和release+acquire同步操作。
其中一些编程语言:
\n\n我想知道是否release+acquire可以违反happens-before:
release+acquire和happens-beforeRelease/acquire建立happens-before不同线程之间的关系:换句话说,保证releasein之前的所有内容在afterThread 1中可见:Thread 2acquire
\\ Thread 1 / \n \\ -------- / \n \\ x = 1 / Everything \n \\ y …Run Code Online (Sandbox Code Playgroud) 我读过这个问答:与“(简单)发生在之前”相比,“强烈发生在之前”的意义是什么?
作者给出了一个有趣的评估的概述,该评估在 C++20 之前是不可能的,但显然从 C++20 开始是可能的:
.-- T3 y.store(3, seq_cst); --. (2)
| | | strongly
| | sequenced before | happens
| V | before
| T3 a = x.load(seq_cst); // a = 0 --. <-' (3)
| : coherence-
| : ordered
| : before
| T1 x.store(1, seq_cst); <-' --. --. (4)
| | |st |
| | sequenced before |h |
| V |b |
| . T1 y.store(1, release); <-' | (x)
| | …Run Code Online (Sandbox Code Playgroud)