mutex_unlock是否可用作内存栅栏?
我将描述的情况发生在iPad 4(ARMv7s)上,使用posix libs进行互斥锁定/解锁。 我在其他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
没有以primefaces方式执行,因此更改为使用AtomicCompareAndSwap
来指定mSharedProducerIndex
。 这一直到现在为止,但事实certificate我错了,错误又回来了。 我猜’修复’只是改变了一些时机。
我现在得出结论,实际问题是互斥锁内写入的乱序执行,即编译器或硬件是否决定重新排序:
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
之外的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
是否肯定会充当写入栅栏?
我还阅读了惠普的一篇文章,该文章建议编译器可以将代码移入(但不会超出) 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
或指令,如PPC上的EIEIO
?
你的产品是同步的,但你没有进行任何同步(你需要同步内存与障碍)。 所以即使你对生产者有完美的记忆障碍,记忆障碍也无法帮助消费者。
在您的代码中,您可能会遇到编译器的排序,硬件排序甚至是其他核心运行的线程#2上的陈旧值mSharedProducerIndex
。
您应该阅读Chapter 11: Memory Ordering
Cortex™-A系列程序员指南中的 Chapter 11: Memory Ordering
,尤其是11.2.1 Memory barrier use example
。
我认为您的问题是您正在获得消费者线程的部分更新。 问题是生产者内部关键部分的内容不是primefaces的,可以重新排序。
not atomic
意味着你的mSharedArray[TempProducerIndex++] = NewData;
它不是一个单词存储(NewData的类型为int),它可以通过几个步骤完成,其他核心可以看作是部分更新。
通过reordering
我的意思是互斥锁提供进出障碍但不会在关键部分强加任何顺序。 由于您在消费者方面没有任何特殊构造,您可以看到mSharedProducerIndex
已更新,但仍然可以看到对mSharedArray[mConsumerIndex]
部分更新。 Mutex仅在执行离开关键部分后才保证内存可见性。
我相信这也解释了为什么它在你添加OSMemoryBarrier();
在临界区内部,因为这种方式cpu被强制将数据写入mSharedArray
然后更新mConsumerIndex
,当其他核心/线程看到mConsumerIndex
我们知道mSharedArray
由于屏障而被完全复制。
我认为你的实现与OSMemoryBarrier();
假设你有很多生产者和一个消费者,这是正确的。 我不同意任何暗示在消费者中设置内存障碍的评论,因为我认为这不会修复生产者内部关键部分发生的部分更新或重新排序。
作为对题目中问题的回答,一般来说,在他们离开之后,他们在进入和写入障碍之前已经阅读了屏障。
“理论”是正确的,写入可以在写入栅栏之后移动到之前。
您的代码的根本问题在于线程2中根本没有同步。您在没有读取障碍的情况下读取mSharedProducerIndex
,因此谁知道您将获得什么值。 你在线程1中做的任何事情都无法解决这个问题。