2017-02-11 80 views
2

多次運行以下代碼會在同一個數字出現多次時產生輸出。我不確定這是爲什麼。爲什麼此代碼多次輸出相同的數字?

#include <iostream> 
#include <pthread.h> 

using namespace std; 

const int NUM_THREADS = 5; 

void* thread_entry(void *i){ 
    cout<<(long)i<<endl; 
} 

int main() { 
    pthread_t threads[NUM_THREADS]; 

    long i; 
    for(i=0;i<NUM_THREADS;i++){ 
    pthread_create(&threads[i],NULL,&thread_entry,(void *)i); 
    } 

    return 0; 
} 

它編譯g++ -std=c++11 main.cpp -lpthread

輸出:

$ ./a.out 
0 
1 
4 
$ ./a.out 
014 
14 
23 
$ ./a.out 
02 
2 
3 
+0

您是否考慮加入您的主題? –

+0

你需要加入你的線程,並且你需要一個互斥體或類似的來保護你的輸出到cout,而且目前是交錯的值。你可能也想考慮使用std :: thread。 –

+0

我沒有看到如何等待他們的終止會改變他們的輸出 – Adam

回答

2

這個方案的主要問題是:

  1. 脫落的功能的端部與聲明的返回類型
  2. 使用std ::法院從多個線程不接合的螺紋
  3. 不同步

3在技術上沒有必要(如果您修復1和2),因爲默認情況下,通過std :: cout輸出是線程安全的,與數據競爭不同,但輸出可以交錯。

現在,對於這個問題

的有趣的部分相同數量的occures不止一次。我不確定這是爲什麼。

假設你正在使用Linux或類似的東西,當主函數存在,它執行exit其中,在GNU C運行時,執行__run_exit_handlers,然後調用_IO_cleanup_IO_cleanup的工作是寫出所有未寫入的輸出緩衝區。如果發生這種情況其中一個其他線程正處於寫入中間,例如在write(2)系統調用中,_IO_cleanup將查看緩衝區狀態並看到它仍然「滿」(_IO_file_write在線程中會更新緩衝區位置指示器,當它從write(2)返回時)。所以根據清理,緩衝區還沒有被寫入,並且它在同一個緩衝區上啓動了自己的系統調用write(2)

1

Cubbi這裏有正確的答案。真正的問題是不加入線程。我會離開這裏,其餘供參考:

現在你可以看到在一個不可預知的順序輸出,但它不應該產生重複的值。一般而言iostream s的不是線程安全:

從C++ 14:27.2.3 [iostreams.threadsafety]

併發訪問一個流對象(27.8,27.9),流緩存器對象(27.6 )或C庫流(27.9.2) 可能會導致數據競爭(1.10),除非另有規定(27.4)。 [注意:數據競賽 導致未定義的行爲(1.10)。 - 注完]

不過27.4給出了適用於這種情況例外:

文件,併發訪問的同步(27.5.3.4)標準iostream對象的格式和無格式IN- 放( 27.7.2.1)和輸出(27.7.3.1)函數或多線程的標準C流在數據爭用(1.10)中不產生 。 [注意:如果用戶希望避免交叉字符,用戶必須仍然同步使用這些對象和多個線程。 - 尾註]

由於cout是同步的,因此應該是線程安全的。

+0

這並不嚴格準確。如果你期望得到任何可理解的打印,你確實需要同步你的輸出,但是標準說,即使沒有明確的同步,從多個線程訪問像cout這樣的對象也是安全的。所以這裏沒有UB,至少就執行輸出而言。 –

+0

@NeilButterworth:我已經添加了相關的標準報價。 –

+0

我無法訪問此機器上的標準,但27.4是什麼意思? –

0

在多線程並不能保證在一個線程的代碼會前或在另一個線程的代碼不之間的任何同步機制後運行。

你有一個主線程和主線程創建其他線程,你不能指望「i」的數量不會前cout'ed 主線程的for循環增加它。

相關問題