gpt4 book ai didi

c - mutex_unlock 是否起到内存栅栏的作用?

转载 作者:太空狗 更新时间:2023-10-29 17:08:51 24 4
gpt4 key购买 nike

我将描述的情况发生在 iPad 4 (ARMv7s) 上,使用 posix 库进行互斥锁定/解锁。不过,我在其他 ARMv7 设备上看到过类似的情况(见下文),所以我认为任何解决方案都需要更全面地了解 ARMv7 的互斥锁和内存栅栏的行为。

场景的伪代码:

线程 1 – 生成数据:

void ProduceFunction() {
MutexLock();
int TempProducerIndex = mSharedProducerIndex; // Take a copy of the int member variable for Producers Index
mSharedArray[TempProducerIndex++] = NewData; // Copy new Data into array at Temp Index
mSharedProducerIndex = TempProducerIndex; // Signal consumer data is ready by assigning new Producer Index to shared variable
MutexUnlock();
}

线程 2 – 消费数据:

void ConsumingFunction () {
while (mConsumerIndex != mSharedProducerIndex) {
doWorkOnData (mSharedArray[mConsumerIndex++]);
}
}

以前(当问题出现在 iPad 2 上时),我认为 mSharedProducerIndex = TempProducerIndex 不是原子执行的,因此改为使用 AtomicCompareAndSwap 来分配mSharedProducerIndex。到目前为止,这一直有效,但事实证明我错了,错误又回来了。我想“修复”只是改变了一些时间。

我现在得出的结论是,实际问题是互斥锁内的写入执行顺序不正确,即如果编译器或硬件决定重新排序:

mSharedArray[TempProducerIndex++] = NewData; // Copy new Data into array at Temp Index 
mSharedProducerIndex = TempProducerIndex; // Signal consumer data is ready by assigning new Producer Index to shared variable

...到:

mSharedProducerIndex = TempProducerIndex; // Signal consumer data is ready by assigning new Producer Index to shared variable
mSharedArray[TempProducerIndex++] = NewData; // Copy new Data into array at Temp Index

...然后消费者与生产者交错,当消费者试图读取数据时,数据还没有写入。

在阅读了一些关于内存屏障的内容之后,我因此认为我会尝试将信号移动到 mutex_unlock 之外的消费者,相信解锁会产生一个内存屏障/栅栏,这将确保 mSharedArray 已写入:

mSharedArray[TempProducerIndex++] = NewData;  // Copy new Data into array at Temp Index 
MutexUnlock();
mSharedProducerIndex = TempProducerIndex; // Signal consumer data is ready by assigning new Producer Index to shared variable

然而,这仍然失败,并让我质疑 mutex_unlock 是否一定会充当写栅栏?

我还阅读了 an article from HP,它建议编译器可以将代码移入(但不能移出)crit_sec。因此,即使在上述更改之后,mSharedProducerIndex 的写入也可能在屏障之前。这个理论有任何意义吗?

通过添加显式栅栏,问题就消失了:

mSharedArray[TempProducerIndex++] = NewData; // Copy new Data into array at Temp Index 
OSMemoryBarrier();
mSharedProducerIndex = TempProducerIndex; // Signal consumer data is ready by assigning new Producer Index to shared variable

因此,我认为我理解这个问题,并且需要围栏,但是任何对解锁行为的洞察以及为什么它似乎没有执行障碍将非常有用。

编辑:

关于消费者线程中缺少互斥体:我依赖于 int mSharedProducerIndex 的写入是一条指令,因此希望消费者能够读取新值或旧值。两者都是有效状态,并且假设 mSharedArray 是按顺序编写的(即在编写 mSharedProducerIndex 之前),这没问题,但从目前所说的来看,我可以' 对此进行回复。

按照同样的逻辑,当前的屏障解决方案似乎也存在缺陷,因为 mSharedProducerIndex 写入可能会在屏障内移动,因此可能会被错误地重新排序。

是否建议向消费者添加互斥量,仅作为读取屏障,或者是否有 pragma 或指令用于禁用生产者的乱序执行,如 EIEIO在 PPC 上?

最佳答案

您的产品已同步,但您在消费时不进行任何同步(您还需要将内存与屏障同步)。因此,即使您为生产者设置了完美的内存屏障,内存屏障也无法帮助消费者。

在您的代码中,您可能会受到编译器排序、硬件排序的影响,甚至会受到运行线程 #2 的其他内核上的陈旧值 mSharedProducerIndex 的影响。

您应该阅读 Cortex™-A Series Programmer’s Guide 中的第 11 章:内存排序 ,尤其是 11.2.1 内存屏障使用示例

我认为您的问题是您在消费者线程中获得了部分更新。问题是生产者关键部分内的内容不是原子的,可以重新排序。

not atomic 我的意思是,如果您的 mSharedArray[TempProducerIndex++] = NewData; 不是单词存储(NewData 的类型为 int),则可能需要分几步完成其他核心可以将其视为部分更新。

通过重新排序,我的意思是互斥体提供了进出障碍,但在关键部分不强加任何顺序。由于您在消费者端没有任何特殊构造,您可以看到 mSharedProducerIndex 已更新,但仍会看到对 mSharedArray[mConsumerIndex] 的部分更新。互斥量仅保证执行离开临界区后的内存可见性。

我相信这也解释了为什么当你在关键部分添加 OSMemoryBarrier(); 时它会起作用,因为这样 cpu 被强制写入数据到 mSharedArray 然后更新 mConsumerIndex 并且当其他核心/线程看到 mConsumerIndex 时,我们知道 mSharedArray 由于障碍而被完全复制。

我认为您使用 OSMemoryBarrier(); 的实现是正确的,假设您有多个生产者和一个消费者。我不同意任何建议在消费者中设置内存障碍的评论,因为我相信这不会修复生产者内部关键部分发生的部分更新或重新排序。

作为标题问题的答案,一般来说,afaik mutexes 在进入之前有读取屏障,在离开之后有写入屏障。

关于c - mutex_unlock 是否起到内存栅栏的作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15003405/

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