2015-01-04 44 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(); 
}