2014-11-02 39 views
3

我開始學習C++ 11標準中的線程,並且我正在嘗試一個非常基本的程序,它創建了10個線程,將它們加入然後退出。在線程功能,我想打印出的該指數環,我在創建線程,就像這樣:爲什麼std :: cout搶佔線程但printf不是?

std::vector<std::thread> threads; 
for(int i = 0; i < 10; i++) 
{ 
    threads.push_back(std::thread([i](){ printf("Thread #%d\n", i); })); 
} 

這從產生併發程序預期的輸出,線程執行了順序:

Thread #0 
Thread #2 
Thread #1 
Thread #3 
Thread #4 
Thread #5 
Thread #6 
Thread #7 
Thread #8 
Thread #9 

但是當我嘗試使用std::coutstd::endl做同樣的事情,我得到這個:

Thread #0 
Thread #Thread #2 
Thread #3 
Thread #9 
1 
Thread #8 
Thread #4 
Thread #5 
Thread #7 
Thread #6 

這是爲什麼^ h與std::cout,但不與printf

+0

爲什麼你會想到第一輸出? – Yakk 2014-11-02 19:47:37

+0

如果你的應用程序在Windows上運行,那麼'printf'似乎有一個互斥體限制對STDOUT的訪問。我從來沒有找到一些文件證實這一點,但這正是我多年來觀察到的...... – 2014-11-02 19:50:10

+0

@Yakk嗯,如果他們按順序,我會更喜歡它,但我的理解是,線程通常不會那樣工作,我錯了嗎? – 2014-11-02 19:50:35

回答

6

你沒有顯示你的std :: cout代碼。

threads.push_back(std::thread([i](){ printf("Thread #%d\n", i); })); 

但是,如果我假設你改變了代碼:

threads.push_back(std::thread([i](){ std::cout << "Thread #" << i << std::endl; })); 

有相當多的不同的兩個:

printf版本只有一個調用印刷庫:

printf("Thread #%d\n", i); 

operator<<有三個不同的調用印刷庫

operator<<(std::cout, "Thread #"); 
operator<<(std::cout, i); 
operator<<(std::cout, std::endl); 

// note for the pedantic the use of functions here is for illustration purposes. 

假設印刷庫具有某種鎖內部的printf版本會給你爲每個線程行。在呼叫之間operator<<版本可能會被搶佔。

即使有內部鎖,我也不會下注任何版本。打印部分可能足夠短,以至於觀察中斷的概率很小,因此您可能尚未觀察到它。

嘗試:

threads.push_back(std::thread([i]() 
{ std::stringstream msg; 
    msg << "Thread #" << i << "\n"; 
    std::cout << msg.rdbuf(); 
})); 
+0

你的假設是正確的。我現在明白了,謝謝你的明確回答 – 2014-11-02 20:27:34

+0

neithe'printf',也不'cout'提供任何保證,即使是單個調用都是線程安全的。事實上,我經常在我的工作中看到線程輸出的「混合」 - 我在爲OpenCL實現'printf'時做了特別的努力,以確保輸出是「一起調用」。例如,它可能適用於短字符串,而在長字符串時「破解」(因爲某些緩衝區中的空間有限)。 – 2014-11-03 00:14:44

+0

關於標準流對象唯一保證的是,如果它與stdio同步,那麼對其格式化或未格式化的I/O函數的併發訪問不構成數據競爭(即,不是未定義的行爲)。打印的字符仍然可以交錯。 – 2014-11-03 07:06:12

0

據我所知,搶佔行爲並不能保證讓我假設你在std :: cout中用std :: endl結束這一行。 std :: endl做的更多,然後只是添加'\ n'它也刷新內部緩衝區,這是我的經驗是線程之間的保護動作。如果你用'\ n'代替std :: endl,它也應該在線程之間混合輸出。更進一步,如果你寫很長的行到你的std :: cout中,你可能會強制緩衝區溢出,如果是std :: cout則會刷新,在這種情況下,輸出也可能會混淆。

+1

我想你誤會了。 'std :: cout'和'std :: endl' * does *混合輸出,'printf'不會。 – 2014-11-02 20:04:23

+1

@MertcanEkiz:就因爲你沒有注意到它並不意味着它不會發生。 – 2014-11-02 20:12:06

+0

@LokiAstari是的。我一直在懷疑它是否會在某些時候發生,所以我一遍又一遍地運行應用程序,但結果並沒有改變 – 2014-11-02 20:13:57

相關問題