2017-04-19 61 views
2

我認爲我的程序有一個錯誤,因爲有時當我運行我的程序時,它會輸出一個比30000小的數字,比如29999.但有時它會正確運行並進入30000.我的問題是如何解決這個問題,爲什麼它發生了嗎?爲什麼我的線程計數器並不總是完成?

#include <iostream> 
#include <thread> 

using namespace std; 

int counter; 
int i; 

void increment() 
{ 
counter++; 
} 

int main() 
{ 
counter = 0; 

cout << "The value in counter is : " << counter << endl; 
thread tarr[30000]; 

    for (i = 0; i < 30000; i++) 
    { 
     tarr[i] = thread(increment); 
    } 

    for (i = 0; i < 30000; i++) 
    { 
     tarr[i].join(); //main thread waits for tarr to finish 
    } 

cout << "After running 30,000 threads "; 
cout << "the value in counter is : " << counter << endl; 
return 0; 
} 
+0

該增量是完全無人防守的* *和競爭條件的食譜。而且你應該感謝幾乎所有線程完成的速度,因爲30000個線程比大多數桌面操作系統能夠處理的大約多27000個,而不需要全身心投入,並且磨合到一個上下文切換的暫停狀態。 – WhozCraig

+0

@aghilpro線程在最後兩個'cout'轉儲之前已經加入。你有什麼可能的好處,你猜測一個'睡眠'會做?\ – WhozCraig

+0

一個良好的睡眠後,事情往往會更好。你醒來時已經清醒過來,準備再次看看代碼,並且......哦,「睡眠」。是的,這不會有所幫助。 – user4581301

回答

2

的問題是,counter++can be broken down into three operations

  1. 負荷初始值寄存器
  2. 增加值
  3. 存儲新的值返回到存儲器

單個線程可能會執行前兩個步驟,然後將控制權交給另一個線程來執行sa我。什麼這可能意味着是:

  1. 線程一讀counter5
  2. 線程一個增加它的內部副本6
  3. 線程兩次讀取counter5
  4. 線程兩個增量其內部副本6
  5. 線程2寫回6counter
  6. 線程一個寫回6counter

你應該讓counterstd::atomic,或者用std::mutex保護它:

std::atomic<int> counter; 
+0

儘管「三個操作中斷」部分聽起來很合理,但根據C++實際上是未定義的行爲。這很重要,因爲這裏提供的模型表明可能結果的範圍比實際情況要小。特別是,這個答案表明計數器必須至少加1個線程,所以結果必須在1到30.000之間。一個合理的優化編譯器可能會刪除所有不安全的賦值給'counter',產生0的同樣有效的結果。不過,這個答案的最終結論是正確的。 – MSalters

相關問題