我正在为一个类编写单元测试,以便在没有可用内存时测试插入。它依赖于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
是一个函数,函数的所有副作用都应该在转到下一行之前完成。投掷是一种副作用。奖励点:如果正确答案是“是否可以重新排序”,那么两条线路之间的内存屏障是否足以解决它?
答案 0 :(得分:2)
没有。重新排序仅在多线程或多处理场景中发挥作用。在单个线程中,编译器不能以改变程序行为的方式重新排序指令。例外情况不是此规则的例外。
当两个线程读写共享状态时,重新排序变为可见。如果线程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无法知道语句已被重新排序。