2017-11-11 196 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

這是我對這個問題的代碼,並且當併發線程數大於012時,它可以正常工作。但是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

順便說一下,不正確的輸出與死鎖不是一回事!如果你有一個協議,程序可能永遠不會結束你的情況。在這種情況下,這意味着您正在等待永遠不可用的數據... – Phil1970

回答

1

如果從多個線程修改變量,則可能必須使用一些同步來確保讀取正確的值。最簡單的方法可能是使用std::atomic作爲變量,以確保操作正確排序。

此外,您的代碼中沒有任何內容可以確保您的doOperations線程在讀取整個文件之前不會完成。

顯然,您需要先讀取整個數據,或者有辦法等待某些數據變爲可用(或達到數據的末尾)。如果讀取初始數據的速度很快但處理速度較慢,那麼較簡單的解決方案是在開始處理線程之前讀取數據。

可能發生的情況是,如果創建了大量線程,則在創建最後一個線程時,initNamesAndOperations應該會讀取整個文件。

我強烈建議您購買並閱讀C++併發行動作者:安東尼威廉姆斯。通過閱讀這本書,你將會對現代C++多線程有一個很好的理解,它會幫助你寫出正確的代碼。