我正在为一个类编写单元测试,以在没有可用内存时测试插入。它依赖于nbElementInserted
返回后insert_edge
递增的事实。
void test()
{
adjacency_list a(true);
MemoryVacuum no_memory_after_this_line;
bool signalReceived = false;
size_t nbElementInserted = 0;
do
{
try
{
a.insert_edge( 0, 1, true ); // this should throw
nbElementInserted++;
}
catch(std::bad_alloc &)
{
signalReceived = true;
}
}
while (!signalReceived); // this loop is necessary because the
// memory vacuum only prevents new memory
// pages from being mapped. so the first
// allocations may succeed.
CHECK_EQUAL( nbElementInserted, a.nb_edges() );
}
现在我想知道以下两个陈述中哪一个是正确的:
nbElementInserted
引发异常之前增加insert_edge
,这会使我的情况无效。可能会发生重新排序,因为如果两行被置换,则用户的可见结果是相同的。 insert_edge
是一个函数,并且该函数的所有副作用都应在转到下一行之前完成。 throw 是一种副作用。 重点:如果正确的答案是“是的,则可能会发生重新排序”,两行之间的存储障碍是否足以解决该问题?
最佳答案
不可以。重新排序仅在多线程或多处理方案中起作用。在单线程中,编译器无法以会改变程序行为的方式对指令重新排序。异常(exception)不是该规则的异常(exception)。
当两个线程读写共享状态时,重新排序变得可见。如果线程A对共享变量进行了修改,则线程B可以无序看到这些修改,如果缓存了共享状态,则根本看不到。这可能是由于线程A或线程B或两者都进行了优化。
但是,线程A将始终看到其自己的修改顺序。每个sequence point必须至少在本地线程知道的范围内顺序发生。
假设线程A执行了以下代码:
a = foo() + bar();
b = baz;
每个
;
都引入一个序列点。因为foo()
不会引入序列点,所以允许编译器先调用bar()
或+
,无论喜欢什么。如果放置打印输出,则可能首先看到foo()
,或者首先看到了bar()
。任一种都是正确的。不过,在将baz
分配给b
之前,它必须先调用它们。如果foo()
或bar()
抛出异常,则b
必须保留其现有值。但是,如果编译器知道
foo()
和bar()
从不抛出,并且它们的执行绝不取决于b
的值,则它可以对这两个语句重新排序。这将是有效的优化。线程A无法知道语句已重新排序。另一方面,线程B会知道。多线程编程中的问题是序列点不适用于其他线程。这就是内存障碍的来源。从某种意义上说,内存障碍是跨线程序列点。
关于c++ - 代码重新排序会影响我的测试吗,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/17494872/