2015-01-04 46 views
0

我正在爲一個遊戲提供概念證明測試程序,其中某些操作被線程化並且信息輸出到每個線程的命令窗口。到目前爲止,我已經獲得了基本的線程處理工作,但似乎我的被調用函數中的couting並不是針對每個線程編寫的,而是每個線程都覆蓋其他輸出。使用線程時的意外輸出

期望或預期的輸出是每個線程將輸出mLaser的mCycle函數內的信息。本質上,這意味着每個對象都需要排序計時器,直到該對象完成任務爲止。每個線程應該有一個輸出,所以如果有五個線程在運行,那麼應該有五個計數器獨立計數。

當前輸出是這樣的:每個線程都在同一個空間輸出自己的信息,然後覆蓋另一個線程試圖輸出的內容。

下面是程序的當前輸出的一個示例:

時間直到週期時間直到週期74完成:36完成:

92秒2秒按下光任意鍵繼續。 。 。

如果您檢查信息是如何從mCycle中傳輸的,您可以看到數字和其他文本位於不應該出現的位置的像差。

什麼應顯示更長時間這些行:

時間直到週期1結束:

92秒

時間直到週期2結束:

112秒

直到第3週期完成的時間:

34秒

週期4已完成!

我不確定這是由於某種線程鎖定是由於我的代碼是如何構造的,或者僅僅是我在輸出編碼中的疏忽而導致的。如果我能得到一雙新的眼睛來查看代碼,並指出任何可能是錯誤的東西,我將不勝感激。

這裏是我的代碼,它應該是編譯的任何MSVS 2013安裝

#include <iostream> 
#include <Windows.h> 
#include <string> 
#include <vector> 
#include <random> 
#include <thread> 
#include <future> 

using namespace std; 

class mLaser 
{ 
public: 
    mLaser(int clen, float mamt) 
    { 
     mlCLen = clen; 
     mlMAmt = mamt; 
    } 

    int getCLen() 
    { 
     return mlCLen; 
    } 

    float getMAmt() 
    { 
     return mlMAmt; 
    } 

    void mCycle(int i1, int mCLength) 
    { 
     bool bMCycle = true; 

     int mCTime_left = mCLength * 1000; 
     int mCTime_start = GetTickCount(); //Get cycle start time 
     int mCTime_old = ((mCTime_start + 500)/1000); 

     cout << "Time until cycle " << i1 << " is complete: " << endl; 

     while (bMCycle) 
     { 
      cout << ((mCTime_left + 500)/1000) << " seconds"; 

      bool bNChange = true; 

      while (bNChange) 
      { 
       //cout << "."; 

       int mCTime_new = GetTickCount(); 

       if (mCTime_old != ((mCTime_new + 500)/1000)) 
       { 
        //cout << mCTime_old << " " << ((mCTime_new+500)/1000) << endl; 
        mCTime_old = ((mCTime_new + 500)/1000); 
        mCTime_left -= 1000; 
        bNChange = false; 
       } 
      } 
      cout << " \r" << flush; 
      if (mCTime_left == 0) 
      { 
       bMCycle = false; 
      } 
     } 

     cout << "Mining Cycle " << i1 << " finished" << endl; 
     system("Pause"); 

     return true; 
    } 


    private: 
    int mlCLen; 
    float mlMAmt; 
}; 

string sMCycle(mLaser ml, int i1, thread& thread); 

int main() 
{ 
    vector<mLaser> mlasers; 
    vector<thread> mthreads; 
    future<string> futr; 

    random_device rd; 
    mt19937 gen(rd()); 

    uniform_int_distribution<> laser(1, 3); 
    uniform_int_distribution<> cLRand(30, 90); 
    uniform_real_distribution<float> mARand(34.0f, 154.3f); 

    int lasers; 
    int cycle_time; 
    float mining_amount; 

    lasers = laser(gen); 

    for (int i = 0; i < lasers-1; i++) 
    {  
     mlasers.push_back(mLaser(cLRand(gen), mARand(gen))); 
     mthreads.push_back(thread()); 
    } 

    for (int i = 0; i < mlasers.size(); i++) 
    { 
     futr = async(launch::async, [mlasers, i, &mthreads]{return sMCycle(mlasers.at(i), i + 1, mthreads.at(i)); }); 

     //mthreads.at(i) = thread(bind(&mLaser::mCycle, ref(mlasers.at(i)), mlasers.at(i).getCLen(), mlasers.at(i).getMAmt())); 
    } 

    for (int i = 0; i < mthreads.size(); i++) 
    { 
     //mthreads.at(i).join(); 
    } 


    //string temp = futr.get(); 
    //float out = strtof(temp.c_str(),NULL); 

    //cout << out << endl; 

    system("Pause"); 
    return 0; 
} 

string sMCycle(mLaser ml, int i1, thread& t1) 
{ 
    t1 = thread(bind(&mLaser::mCycle, ref(ml), ml.getCLen(), ml.getMAmt())); 
    //t1.join(); 

    return "122.0"; 
} 

回答

3

(不使用自定義庫),雖然從多個線程編寫併發到std::cout必須是數據爭自由,難保併發寫入不會被交錯。我不確定一個線程的一個寫操作是否可以與另一個線程的一個寫操作交錯,但是它們當然可以在寫操作(我認爲來自不同線程的各個輸出可以交錯)之間交錯。

關於併發訪問標準流對象的標準有什麼要說的(即,std::coutstd::cin等)是在27.4.1 [iostream.objects.overview]段落4:

併發訪問的同步(27.5.3.4)標準iostream對象的格式和無格式輸入(27.7.2.1)並且多線程輸出(27.7.3.1)函數或標準C流不會導致數據競爭(1.10)。 [注意:如果用戶希望避免交錯字符,用戶必須仍然同步使用這些對象和多個線程的流。 - 注意]

如果您希望以某種單位顯示輸出,則需要通過例如使用互斥鎖來同步對std::cout的訪問。

+0

感謝您的建議,Mutexs是我可以修復的名單上,但是我發現了一個可能的,簡單的,方法來解決這個問題使用shared_future可以複製(所以我可以推入一個向量)。 – Geowil

0

雖然Dietmar的回答足夠了,但我決定採用另一種更簡單的方式。由於我正在創建一個類的實例,並且正在線程中訪問這些實例,所以我選擇在線程期間更新這些類的數據,然後在線程執行完畢後調用更新的數據。

這樣我就不必處理像數據競賽這樣的煩人問題,也不需要從shared_future的矢量中獲取異步輸出。這裏是萬一別人我修改後的代碼想實現類似的東西:

#include <iostream> 
#include <Windows.h> 
#include <string> 
#include <vector> 
#include <random> 
#include <thread> 
#include <future> 

using namespace std; //Tacky, but good enough fo a poc D: 

class mLaser 
{ 
public: 
    mLaser(int clen, float mamt, int time_left) 
    { 
     mlCLen = clen; 
     mlMAmt = mamt; 
     mCTime_left = time_left; 
     bIsCompleted = false; 
    } 

    int getCLen() 
    { 
     return mlCLen; 
    } 

    float getMAmt() 
    { 
     return mlMAmt; 
    } 

    void setMCOld(int old) 
    { 
     mCTime_old = old; 
    } 

    void mCycle() 
    { 
     if (!bIsCompleted) 
     { 
      int mCTime_new = GetTickCount(); //Get current tick count for comparison to mCOld_time 

      if (mCTime_old != ((mCTime_new + 500)/1000)) //Do calculations to see if time has passed since mCTime_old was set 
      { 
       //If it has then update mCTime_old and remove one second from mCTime_left. 
       mCTime_old = ((mCTime_new + 500)/1000); 
       mCTime_left -= 1000; 
      } 

      cur_time = mCTime_left; 
     } 

     else 
     { 
      mCTime_left = 0; 
     } 
    } 

    int getCTime() 
    { 
     return cur_time; 
    } 

    int getCTLeft() 
    { 
     return mCTime_left; 
    } 

    void mCComp() 
    { 
     bIsCompleted = true; 
    } 

    bool getCompleted() 
    { 
     return bIsCompleted; 
    } 

private: 
    int mlCLen; //Time of a complete mining cycle 
    float mlMAmt; //Amoung of ore produced by one mining cycle (not used yet) 
    int cur_time; //The current time remaining in the current mining cycle; will be removing this as it is just a copy of mCTime_left that I was going to use for another possiblity to make this code work 
    int mCTime_left; //The current time remaining in the current mining cycle 
    int mCTime_old; //The last time that mCycle was called 

    bool bIsCompleted; //Flag to check if a mining cycle has already been accounted for as completed 
}; 

void sMCycle(mLaser& ml, int i1, thread& _thread); //Start a mining cycle thread 

//Some global defines 
random_device rd; 
mt19937 gen(rd()); 

uniform_int_distribution<> laser(1, 10); //A random range for the number of mlaser entities to use 
uniform_int_distribution<> cLRand(30, 90); //A random time range in seconds of mining cycle lengths 
uniform_real_distribution<float> mARand(34.0f, 154.3f); //A random float range of the amount of ore produced by one mining cycle (not used yet) 

int main() 
{ 
    //Init some variables for later use 
    vector<mLaser> mlasers; //Vector to hold mlaser objects 
    vector<thread> mthreads; //Vector to hold threads 
    vector<shared_future<int>> futr; //Vector to hold shared_futures (not used yet, might not be used if I can get the code working like this) 

    int lasers; //Number of lasers to create 
    int cycle_time; //Mining cycle time 
    int active_miners = 0; //Number of active mining cycle threads (one for each laser) 
    float mining_amount; //Amount of ore produced by one mining cycle (not used yet) 

    lasers = laser(gen); //Get a random number 
    active_miners = lasers; //Set this to that random number for the while loop later on 

    //Create the mlaser objects and push them into the mlasers vector 
    for (int i = 0; i < lasers; i++) 
    { 
     int clength = cLRand(gen); 

     mlasers.push_back(mLaser(clength, mARand(gen), (clength * 1000))); 

     //Also push thread obects into mthreads for each laser object 
     mthreads.push_back(thread()); 
    } 

    //Setup data for mining cycles 
    for (int i = 0; i < mlasers.size(); i++) 
    { 
     int mCTime_start = GetTickCount(); //Get cycle start time 
     mlasers.at(i).setMCOld(((mCTime_start + 500)/1000)); 
    } 

    //Print initial display for mining cycles 
    for (int i = 0; i < mlasers.size(); i++) 
    { 
     cout << "Mining Laser " << i + 1 << " cycle will complete in " << (mlasers.at(i).getCTLeft() + 500)/1000 << " seconds..." << endl; 
    } 

    while (active_miners > 0) 
    { 
     for (int i = 0; i < mlasers.size(); i++) 
     { 
      //futr.push_back(async(launch::async, [mlasers, i, &mthreads]{return sMCycle(mlasers.at(i), i + 1, mthreads.at(i)); })); 
      async(launch::async, [&mlasers, i, &mthreads]{return sMCycle(mlasers.at(i), i + 1, mthreads.at(i)); }); //Launch a thread for the current mlaser object 
      //mthreads.at(i) = thread(bind(&mLaser::mCycle, ref(mlasers.at(i)), mlasers.at(i).getCLen(), mlasers.at(i).getMAmt())); 
     } 

     //Output information from loops 
     //cout << " \r" << flush; //Return cursor to start of line and flush the buffer for the next info 

     system("CLS"); 

     for (int i = 0; i < mlasers.size(); i++) 
     { 
      if (mlasers.at(i).getCTLeft() != 0) //If mining cycle is not completed 
      { 
       cout << "Mining Laser " << i + 1 << " cycle will complete in " << (mlasers.at(i).getCTLeft() + 500)/1000 << " seconds..." << endl; 
      } 

      else if (mlasers.at(i).getCTLeft() == 0) //If it is completed 
      { 
       if (!mlasers.at(i).getCompleted()) 
       { 
        mlasers.at(i).mCComp(); 
        active_miners -= 1; 
       } 

       cout << "Mining Laser " << i + 1 << " has completed its mining cycle!" << endl; 
      } 
     } 
    } 


    /*for (int i = 0; i < mthreads.size(); i++) 
    { 
     mthreads.at(i).join(); 
    }*/ 


    //string temp = futr.get(); 
    //float out = strtof(temp.c_str(),NULL); 

    //cout << out << endl; 

    system("Pause"); 
    return 0; 
} 

void sMCycle(mLaser& ml, int i1,thread& _thread) 
{ 
    //Start thread 
    _thread = thread(bind(&mLaser::mCycle, ref(ml))); 

    //Join the thread 
    _thread.join(); 
}