2015-01-04 7 views
0

Я работаю над доказательством программы тестирования понятий для игры, в которой определенные действия передаются в потоке, и информация выводится в окно команд для каждого потока. До сих пор я получил основной процесс потоковой обработки, но кажется, что couting в моей вызываемой функции не записывается для каждого потока, и вместо этого каждый поток перезаписывает другие.Неожиданный вывод при использовании потоков

Желаемый или ожидаемый результат состоит в том, что каждый поток выводит информацию, изнашиваемую в функции mCycle от mLaser. По сути, это предназначено для таймера сортировки для каждого объекта, отсчитывающего время, пока этот объект не выполнит свою задачу. Должен быть выход для каждого потока, поэтому, если пять потоков работают, должно быть пять счетчиков, отсчитывающих независимо.

Токовый выход таков, что каждый поток выводит свою собственную информацию в том же пространстве, которое затем перезаписывает то, что пытается выполнить другой поток.

Ниже приведен пример токового выхода программы:

Время до времени цикла до цикла 74, пока не завершится: 36 завершена:

92 секунд 2 секунды ССГ любую клавишу для продолжения. , ,

Вы можете видеть аберрации, в которых номера и другой текст находятся там, где их не должно быть, если вы изучите, как информация вычеркнута из mCycle.

Что должно отображаться более длинный эти строки:

Времени до цикл-не является полным:

92 секунды

Времени до цикл-не является полным:

112 секунд

Время до завершения цикла 3:

34 секунды

Цикл 4 завершен!

Я не уверен, что это связано с тем, что какая-то блокировка потоков происходит из-за того, как мой код структурирован или просто контролируется в моем кодировании вывода. Если бы я мог получить свежую пару глаз, чтобы просмотреть код и указать что-то, что может быть ошибкой, я был бы признателен.

Вот мой код, он должен быть скомпилирован в любом МСВСЕ 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::cout, std::cin и т.д.) в 27.4.1 [iostream.objects.overview] пункт 4:

Одновременный доступ к синхронизированным (форматированный и неформатированный ввод 27.5.3.4) Стандартный iostream объекта (27.7.2.1) и функции вывода (27.7.3.1) или стандартный поток C несколькими потоками не должны приводить к гонке данных (1.10). [Примечание. Пользователи должны синхронизировать одновременное использование этих объектов и потоков несколькими потоками, если они хотят избежать чередующихся символов. -end примечание]

Если вы хотите иметь выход появиться в какой-то блок, вам нужно будет синхронизировать доступ к std::cout, например, с помощью мьютекса.

+0

Спасибо за предложение, Mutexs были в моем списке возможных исправлений, однако я нашел возможный, более простой, способ обойти эту проблему используя shared_future, который можно скопировать (чтобы я мог заталкивать его в вектор). – Geowil

0

Хотя ответа Дитмара достаточно, я решил пойти другим, гораздо более простым, маршрутом. Поскольку я создаю экземпляры класса, и я обращаюсь к этим экземплярам в потоках, я решил обновить данные этого класса во время потоковой передачи, а затем вызвал обновленные данные, как только поток завершил выполнение.

Таким образом, мне не нужно иметь дело с раздражающими проблемами, такими как расы данных, или захватывать выходные данные из async в векторе 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(); 
} 
Смежные вопросы