- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我将描述的情况发生在 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 mutex
es 在进入之前有读取屏障,在离开之后有写入屏障。
关于c - mutex_unlock 是否起到内存栅栏的作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15003405/
我有一个 if 语句,如下所示 if (not(fullpath.lower().endswith(".pdf")) or not (fullpath.lower().endswith(tup
然而,在 PHP 中,可以: only appears if $foo is true. only appears if $foo is false. 在 Javascript 中,能否在一个脚
XML有很多好处。它既是机器可读的,也是人类可读的,它具有标准化的格式,并且用途广泛。 它也有一些缺点。它是冗长的,不是传输大量数据的非常有效的方法。 XML最有用的方面之一是模式语言。使用模式,您可
由于长期使用 SQL2000,我并没有真正深入了解公用表表达式。 我给出的答案here (#4025380)和 here (#4018793)违背了潮流,因为他们没有使用 CTE。 我很欣赏它们对于递
我有一个应用程序: void deleteObj(id){ MyObj obj = getObjById(id); if (obj == null) { throw n
我的代码如下。可能我以类似的方式多次使用它,即简单地说,我正在以这种方式管理 session 和事务: List users= null; try{ sess
在开发J2EE Web应用程序时,我通常会按以下方式组织我的包结构 com.jameselsey.. 控制器-控制器/操作转到此处 服务-事务服务类,由控制器调用 域-应用程序使用的我的域类/对象 D
这更多是出于好奇而不是任何重要问题,但我只是想知道 memmove 中的以下片段文档: Copying takes place as if an intermediate buffer were us
路径压缩涉及将根指定为路径上每个节点的新父节点——这可能会降低根的等级,并可能降低路径上所有节点的等级。有办法解决这个问题吗?有必要处理这个吗?或者,也许可以将等级视为树高的上限而不是确切的高度? 谢
我有两个类,A 和 B。A 是 B 的父类,我有一个函数接收指向 A 类型类的指针,检查它是否也是 B 类型,如果是将调用另一个函数,该函数接受一个指向类型 B 的类的指针。当函数调用另一个函数时,我
有没有办法让 valgrind 使用多个处理器? 我正在使用 valgrind 的 callgrind 进行一些瓶颈分析,并注意到我的应用程序中的资源使用行为与在 valgrind/callgrind
假设我们要使用 ReaderT [(a,b)]超过 Maybe monad,然后我们想在列表中进行查找。 现在,一个简单且不常见的方法是: 第一种可能性 find a = ReaderT (looku
我的代码似乎有问题。我需要说的是: if ( $('html').attr('lang').val() == 'fr-FR' ) { // do this } else { // do
根据this文章(2018 年 4 月)AKS 在可用性集中运行时能够跨故障域智能放置 Pod,但尚不考虑更新域。很快就会使用更新域将 Pod 放入 AKS 中吗? 最佳答案 当您设置集群时,它已经自
course | section | type comart2 : bsit201 : lec comart2 :
我正在开发自己的 SDK,而这又依赖于某些第 3 方 SDK。例如 - OkHttp。 我应该将 OkHttp 添加到我的 build.gradle 中,还是让我的 SDK 用户包含它?在这种情况下,
随着 Rust 越来越充实,我对它的兴趣开始激起。我喜欢它支持代数数据类型,尤其是那些匹配的事实,但是对其他功能习语有什么想法吗? 例如标准库中是否有标准过滤器/映射/归约函数的集合,更重要的是,您能
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 这个问题似乎与 help center 中定义的范围内的编程无关。 . 关闭 9 年前。 Improve
我一直在研究 PHP 中的对象。我见过的所有示例甚至在它们自己的对象上都使用了对象构造函数。 PHP 会强制您这样做吗?如果是,为什么? 例如: firstname = $firstname;
...比关联数组? 关联数组会占用更多内存吗? $arr = array(1, 1, 1); $arr[10] = 1; $arr[] = 1; // <- index is 11; does the
我是一名优秀的程序员,十分优秀!