2017-11-11 13 views
0

동시 C++ 프로그래밍에 대한 도움이 필요합니다. 나는이 형식으로 "names.txt"라는 이름의 파일을 가지고 :뮤텍스 및 대기를위한 데이터 사용시 교착 상태가 발생했습니다.

0 James 
1 Sara 
2 Isaac 

그리고이 형식으로, 파일 이름에 대한 몇 가지 작업을 포함 "op.txt"라는 이름의 다른 파일이 있습니다

0 1 + // this means add Sara to James and store it in 0 position 
1 2 $ // this means swap values in position 1 and position 2 

과 이 형식으로, 작업의 출력이 "output.txt" 파일 :

0 JamesSara 
1 Isaac 
2 Sara 

문제는 그 생성 말한다 읽기 용 스레드 names.txtop.txt을 저장하고 저장하십시오. 그런 다음 작업을 동시에 수행하고 마지막에는 스레드에 output.txt을 수행 할 수있는 가변 스레드를 만듭니다.

이 코드는 다음과 같습니다. 동시 스레드 수가 이상일 때 올바르게 작동하지만 1과 2 스레드의 출력이 올바르지 않습니다. 이 코드에서 내가 놓친 부분은 무엇입니까?

#include <fstream> 
#include <iostream> 
#include <vector> 
#include <sstream> 
#include <cstdlib> 
#include <thread> 
#include <mutex> 
#include <condition_variable> 
#include <deque> 

using namespace std; 

std::mutex _opMutex; 
std::condition_variable _initCondition; 
std::condition_variable _operationCondition; 

int _counter = 0; 
int _initCounter = 0; 
int _doOperationCounter = 0; 

struct OperationStruct 
{ 
    int firstOperand; 
    int secondOperand; 
    char cOperator; 
}; 

const int THREADS = 5; 

std::deque<std::pair<int, string> > _nameVector; 
std::deque<OperationStruct> _opStructVec; 

void initNamesAndOperations() 
{ 
    ifstream infile; 

    std::pair<int, string> namePair; 

    infile.open("names.txt"); 
    if (!infile) 
    { 
     cout << "Unable to open file"; 
     exit(-1); 
    } 

    int id; 
    string value; 

    while (infile >> id >> value) 
    { 
     namePair.first = id; 
     namePair.second = value; 
     _nameVector.push_back(namePair); 
    } 
    infile.close(); 

    infile.open("op.txt"); 

    if (!infile) 
    { 
     cout << "Unable to open file"; 
     exit(-1); 
    } 

    int firstOperand; 
    int secondOperand; 
    char cOperator; 

    while (infile >> firstOperand >> secondOperand >> cOperator) 
    { 
     OperationStruct opSt; 
     opSt.firstOperand = firstOperand; 
     opSt.secondOperand = secondOperand; 
     opSt.cOperator = cOperator; 
     _opStructVec.push_back(opSt); 
     ++_initCounter; 
    } 
    infile.close(); 

    return; 
} 

void doOperationMath(int firstIndex, string firstValue, string secondValue, char cOp) 
{ 
    //basic mathematics 
    switch (cOp) 
    { 
    case '+': 
    { 
       for (int i = 0; i < _nameVector.size(); ++i) 
       { 
        std::pair<int, string> acc = _nameVector[i]; 
        if (acc.first == firstIndex) 
        { 
         acc.second = firstValue + secondValue; 
         _nameVector[i].second = acc.second; 
        } 
       } 
    } 
    break; 

    default: 
     break; 
    } 

    ++_doOperationCounter; 
} 

void doOperationSwap(int firstIndex, int secondIndex, string firstValue, string secondValue) 
{ 
    //swap 
    for (int i = 0; i < _nameVector.size(); ++i) 
    { 
     if (_nameVector[i].first == firstIndex) 
      _nameVector[i].second = secondValue; 

     if (_nameVector[i].first == secondIndex) 
      _nameVector[i].second = firstValue; 
    } 
    ++_doOperationCounter; 
} 

void doOperations() 
{ 
    while (_doOperationCounter < _initCounter) 
    { 
     std::unique_lock<mutex> locker(_opMutex); 
     _initCondition.wait(locker, [](){return !_opStructVec.empty(); }); 
     OperationStruct opSt = _opStructVec.front(); 
     _opStructVec.pop_front(); 
     locker.unlock(); 
     _operationCondition.notify_one(); 
     int firstId = opSt.firstOperand; 
     int secondId = opSt.secondOperand; 
     char cOp = opSt.cOperator; 

     string firstValue = ""; 
     string secondValue = ""; 

     for (int j = 0; j < _nameVector.size(); ++j) 
     { 
      std::pair<int, string> acc = _nameVector[j]; 
      if (firstId == acc.first) 
       firstValue = acc.second; 

      if (secondId == acc.first) 
       secondValue = acc.second; 
     } 

     if (cOp == '$') 
     { 
      doOperationSwap(firstId, secondId, firstValue, secondValue); 
     } 
     else 
     { 
      doOperationMath(firstId, firstValue, secondValue, cOp); 
     } 

    } 

    return; 
} 

void doOutputFile() 
{ 
    ofstream outfile; 

    outfile.open("sampleOutput.txt", std::ios::out | std::ios::app); 
    if (!outfile) 
    { 
     cout << "Unable to open the file"; 
     exit(-1); 
    } 

    while (_counter < _initCounter) 
    { 
     std::unique_lock<mutex> locker(_opMutex); 
     _operationCondition.wait(locker, [](){return !_nameVector.empty(); }); 
     auto accPair = _nameVector.front(); 
     _nameVector.pop_front(); 
     locker.unlock(); 

     outfile << accPair.first << " " << accPair.second << endl; 
     ++_counter; 
    } 

    return; 
} 

int main() 
{ 
    thread th1(initNamesAndOperations); 

    std::vector<thread> operationalThreads; 
    for (int i = 0; i < THREADS; ++i) 
    { 
     operationalThreads.push_back(thread(doOperations)); 
    } 

    thread th3(doOutputFile); 

    th1.join(); 

    for (auto& opthread : operationalThreads) 
     opthread.join(); 

    th3.join(); 

    return 0; 
} 
+2

디버거를 사용해 보셨습니까? –

+0

예, 어떻게 든 문제가 무엇인지 압니다. 프로그램이 첫 번째 스레드의 중간에있는 동안 IO에서 읽는 것이 약간 시간 소모적이기 때문에 다른 스레드 조인과 doOperations() 함수 호출이 필요합니다. while 조건이 잘못되어 스레드가 반환됩니다. – King

+0

그건 그렇고, 잘못된 출력은 교착 상태와 같은 것이 아닙니다! 당신은 dealock 있다면, 프로그램은 아마 당신의 경우에 끝나지 않을 것입니다. 이 경우, 귀하가 결코 사용할 수없는 데이터를 기다리고 있다는 것을 의미합니다 ... – Phil1970

답변

1

변수가 여러 스레드에서 수정 된 경우 적절한 값을 읽으려면 동기화를 사용해야 할 수 있습니다. 가장 간단한 방법은 변수에 std::atomic을 사용하여 작업의 순서가 올바르게 지정되도록하는 것입니다.

또한 전체 파일을 읽기 전에 doOperations 스레드가 완료되지 않도록 코드에 아무것도 없습니다.

분명히 전체 데이터를 먼저 읽거나 일부 데이터가 사용 가능할 때까지 (또는 데이터의 끝에 도달 할 때까지) 기다려야합니다. 초기 데이터 읽기가 빠르지 만 처리 속도가 느린 경우 스레드 처리를 시작하기 전에 데이터를 읽는 것이 더 쉬운 솔루션입니다.

아마도 많은 스레드를 만들면 마지막 스레드를 만들 때까지 initNamesAndOperations이 전체 파일을 읽었을 것입니다.

나는 매우 구입하고 앤서니 윌리엄스에 의해 동작에 C++ 동시성을 읽어보실 것을 권장합니다. 이 책을 읽으면 현대 C++ 멀티 쓰레딩에 대해 잘 이해할 수 있으며 올바른 코드를 작성하는 데 많은 도움이됩니다.