2013-07-05 62 views
1

我正在爲一個類編寫單元測試,以在沒有內存可用時測試插入。它取決於nbElementInsertedinsert_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是一個函數,應在完成下一行之前完成函數的所有副作用。投擲是一個副作用。

獎勵點:如果正確答案是「是的重新排序可能發生」,是2號線足以解決它之間的內存屏障?

+0

C/C++規範要求優化都具有「假設」優化沒有發生的結果。但是,這隻適用於實施,並且只有在計劃的正常執行中才需要。像POSIX SIGHUP和線程這樣的操作系統信號超出了C++規範,並且打破了「假設」的假設。就你而言,即使你稱之爲「signalReceived」,它也不是POSIX信號,不會成爲問題。 –

回答

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沒有辦法知道語句已被重新排序。

另一方面,線程B會知道。多線程編程中的問題是序列點不適用於其他線程。這就是內存屏障進入的地方。從某種意義上說,內存屏障是跨線程的順序點。

+0

我很困惑:編譯器如何預先知道代碼是否是多線程的? – qdii

+1

@qdii不同的線程可能會觀察到這個線程如何修改共享狀態的亂序行爲,但是這個線程總是會看到自己的行爲按順序發生。 –