我正在为一个类编写单元测试,以在没有可用内存时测试插入。它依赖于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/

    10-13 04:03