在不使用volatile或内存屏障和锁定的情况下保证执行顺序

时间:2017-03-10 08:11:24

标签: c multithreading performance compiler-optimization

我有关于编译器改变执行顺序的问题。我试图通过用信号机制(thorugh信号量)替换临界区来提高多线程程序(C语言)的性能。

我需要保证这里的执行顺序,并且已经对此进行了一些研究。我在函数中看到了很多关于执行顺序的问题,但对函数中的函数没有多少讨论。

根据 https://en.wikipedia.org/wiki/Sequence_point规则#4 ,以下代码块是否可以保证在*p->a输入func2之前必须先评估func2 pfunc1 (struct *p) { p->a = x; func2 (p); } func2 (struct *p) { p->b = y; releaseSemaphore(s); } 作为输入(假设编译器遵守此处定义的调度点规则)?

p->b

至关重要的是,p->a仅在设置p->b后设置,因为另一个线程在处理各种请求的循环中,并通过是否设置p->b来标识有效请求。释放信号量只会在空闲(并等待信号量)时触发任务,但是如果它忙于处理其他请求,它将在稍后检查func1,并且我们不能保证仅调用HashMap propertyMap = new HashMap(); propertyMap.put(CassandraConstants.CQL_VERSION, CassandraConstants.CQL_VERSION_3_0); EntityManagerFactory emf = Persistence.createEntityManagerFactory("cassandra_pu", propertyMap); 当该线程空闲时。

2 个答案:

答案 0 :(得分:2)

没有。序列点排序不会在线程边界上转换。这就是为什么我们首先需要内存排序保证的原因。

对于执行代码的线程,始终保证序列点排序(模数为as-if-rule)。任何其他线程都可能以任意顺序观察该线程的写入。这意味着即使线程#1可以验证它以特定顺序执行写入,线程#2仍可能以不同的顺序观察它们。这就是为什么挥发性在这里还不够。

从技术上讲,这可以解释为例如。通过缓存。线程#1的写入可能首先进入写缓冲区,它们仍然对线程#2不可见。只有将写入缓冲区刷回主存储器后,它们才会变为可见,并且允许硬件在刷新之前对写入进行重新排序。

请注意,仅仅因为允许平台重新排序写入并不意味着它会。这是危险的部分。在一个平台上运行完美的代码在移植到另一个平台时可能会突然出现。使用适当的内存顺序可确保代码无处不在

答案 1 :(得分:1)

实现可以 1 更改排序,只要这不是通过其他翻译单元的函数调用完成的。

这种重新排序与多线程正交,即它在单线程和多线程程序中完成。

如果函数func2与func1在同一个转换单元中,则可以执行以下操作:

func1 (struct *p) 
{
    func2 (p);
    p->a = x;  
}

如果您想阻止 2 这样的重新排序,请使用volatile if。 (请注意,这样做是为了防止上面提到的重新排序,而不是出于其他同步目的。您将不得不使用原子基元。)

1 (引自:ISO / IEC 9899:201x 5.1.2.3程序执行10)
或者,实现可以在每个翻译单元内执行各种优化,例如 只有在进行函数调用时,实际的语义才会与抽象语义一致 翻译单位边界。

2 (引自:ISO / IEC 9899:201x 6.7.3类型限定符7)
具有volatile限定类型的对象可以以未知的方式进行修改 实施或有其他未知的副作用。因此任何表达都是指 对这样的对象应严格按照抽象机的规则进行评估, 如5.1.2.3中所述。此外,在每个序列点上最后存储的值 对象应与抽象机器规定的内容一致,除非经过修改 以前提到的未知因素。