簡體   English   中英

使用互斥量和等待數據時出現死鎖

[英]deadlock in using data for mutex and waits

對於並發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

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

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

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

顯然,您需要先讀取整個數據,或者有一種方法來等待某些數據可用(或到達數據末尾)。 如果讀取初始數據快而處理慢,那么更簡單的解決方案是在開始處理線程之前讀取數據。

可能發生的情況是,如果創建了很多線程,那么在創建最后一個線程時, initNamesAndOperations將會讀取整個文件。

我強烈建議您購買和閱讀Anthony Williams 撰寫的C ++ Concurrency in Action 通過閱讀此類書籍,您將對現代C ++多線程有一個很好的了解,並且將有助於您編寫正確的代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM