从多个线程访问和使用std :: vector

时间:2014-10-20 00:21:01

标签: c++ multithreading vector

我有一个矢量,我想从多个线程访问和使用。我在下面创建了一个示例来说明我想要完成的功能。

目标是(1)高速,(2)线程安全,(3)如果可能继续使用向量,因为我的大项目使用遍布各处的向量,因此我想保持这一点。

但是,如果我需要从向量切换到其他东西,那么我也会对此持开放态度。

以下示例编译但崩溃相当快,因为​​它不是线程安全的。

关于如何修复下面的示例的任何想法,然后我可以将其应用于我的大型项目?

谢谢你:

#include <vector>
#include <ctime>
#include <thread>
#include <iostream>
#include <random>
#include <atomic>
#include <algorithm>

enum EmployeeType {

    HOURLY = 0,
    SALARIED = 1,
    COMMISSION = 2,
    OTHER = 3

};

enum EmployeePosition {

    SalesPerson = 0,
    Cashier = 1,
    Stocker = 2,
    Janitor = 3,
    AssistantManager = 4,
    Manager = 5,
    GeneralManager = 6,
    Owner = 7

};

class Employee;

class Employee {

private:

    float _TotalCostPerYear;
    EmployeeType _TypeOfEmployee;
    EmployeePosition _PositionOfEmployee;

protected:

public:

    Employee() :_TotalCostPerYear(0.0f), _TypeOfEmployee(EmployeeType::HOURLY),
        _PositionOfEmployee(EmployeePosition::SalesPerson){};

    float GetTotalCost() { return _TotalCostPerYear; }
    void SetTotalCost(float ValueToSet) { _TotalCostPerYear = ValueToSet; }

    EmployeeType GetEmployeeType() { return _TypeOfEmployee; }
    void SetEmployeeType(EmployeeType ValueToSet) { _TypeOfEmployee = ValueToSet; }

    EmployeePosition GetEmployeePosition() { return _PositionOfEmployee; }
    void SetEmployeePosition(EmployeePosition ValueToSet) { _PositionOfEmployee = ValueToSet; }

};

std::vector <Employee> AllEmployees;

std::thread* AddEmployeesThread;
std::thread* RemoveEmployeesThread;
std::thread* CalculateEmploymentCostsThread;
std::thread* PrintTotalsThread;

std::atomic<bool> ContinueProcessing = true;
std::atomic<float> TotalSalaryCosts = 0.0f;

std::uniform_int_distribution<int>* RandDistTypeOfEmployee;
std::uniform_int_distribution<int>* RandDistPositionOfEmployee;
std::uniform_real_distribution<float>* RandDistSalaryOfEmployee;

std::mt19937* RandomNumberGenerator;

time_t rawtime;
struct tm timeinfo;

void RandomAddEmployees();
void RandomRemoveEmployees();
void CalculateEmployementCosts();
void PrintTotals();

void RandomAddEmployees() {

    while (ContinueProcessing) {

        Employee NewEmployee;

        NewEmployee.SetEmployeePosition((EmployeePosition)(*RandDistPositionOfEmployee)(*RandomNumberGenerator));
        NewEmployee.SetEmployeeType((EmployeeType)(*RandDistTypeOfEmployee)(*RandomNumberGenerator));
        NewEmployee.SetTotalCost((*RandDistSalaryOfEmployee)(*RandomNumberGenerator));

        AllEmployees.push_back(NewEmployee);

    }

}

void RandomRemoveEmployees() {

    while (ContinueProcessing) {

        EmployeePosition PositionToRemove = (EmployeePosition)(*RandDistPositionOfEmployee)(*RandomNumberGenerator);

        static const auto is_position_erasable = [&PositionToRemove](Employee& E) { return E.GetEmployeePosition() == PositionToRemove; };

        AllEmployees.erase(std::remove_if(AllEmployees.begin(), AllEmployees.end(), is_position_erasable), AllEmployees.end());

        EmployeeType TypeToRemove = (EmployeeType)(*RandDistTypeOfEmployee)(*RandomNumberGenerator);

        static const auto is_type_erasable = [&TypeToRemove](Employee& E) { return E.GetEmployeeType() == TypeToRemove; };

        AllEmployees.erase(std::remove_if(AllEmployees.begin(), AllEmployees.end(), is_position_erasable), AllEmployees.end());

    }

}

void CalculateEmployementCosts() {

    while (ContinueProcessing) {

        float RunningTotal = 0.0f;

        for (unsigned int i = 0; i < AllEmployees.size(); ++i) {

            RunningTotal += AllEmployees[i].GetTotalCost();

        }

        TotalSalaryCosts = RunningTotal;

    }

}

void PrintTotals() {

    while (ContinueProcessing) {

        time(&rawtime);
        localtime_s(&timeinfo, &rawtime);

        if ((timeinfo.tm_sec % 5) == 0) {

            std::cout << "\n\nIn total there are " << AllEmployees.size() << " employees with a total cost of " << TotalSalaryCosts << " to the company.";

        }

    }

}


int main(int argc, char** argv) {

    time(&rawtime);
    localtime_s(&timeinfo, &rawtime);

    RandomNumberGenerator = new std::mt19937((unsigned int)timeinfo.tm_sec);
    RandDistTypeOfEmployee = new std::uniform_int_distribution<int>(0, 3);
    RandDistPositionOfEmployee = new std::uniform_int_distribution<int>(0, 7);
    RandDistSalaryOfEmployee = new std::uniform_real_distribution<float>(35000.0f, 300000.0f);

    std::cout << "Welcome to the crude employment simulation program. Press enter to get started.";
    std::cout << "\n\nNote that once the program starts you can press any key to stop the simulation.\n";

    std::cin.get();

    AddEmployeesThread = new std::thread(RandomAddEmployees);
    RemoveEmployeesThread = new std::thread(RandomRemoveEmployees);
    CalculateEmploymentCostsThread = new std::thread(CalculateEmployementCosts);
    PrintTotalsThread = new std::thread(PrintTotals);

    std::cin.get();

    std::cout << "\n\nExiting the simulation.";

    ContinueProcessing = false;

    AddEmployeesThread->join();
    RemoveEmployeesThread->join();
    CalculateEmploymentCostsThread->join();
    PrintTotalsThread->join();

    delete AddEmployeesThread;
    delete RemoveEmployeesThread;
    delete CalculateEmploymentCostsThread;
    delete PrintTotalsThread;

    delete RandDistSalaryOfEmployee;
    delete RandDistPositionOfEmployee;
    delete RandDistTypeOfEmployee;

}

1 个答案:

答案 0 :(得分:2)

您需要保护对AllEmployees变量的访问权限,以便只有一个线程可以随时访问它。您可以使用std::mutex进行保护,并在必要时使用std::lock_guard<std::mutex>将其锁定。首先,添加此包含文件:

#include <mutex>

接下来,定义一个互斥锁 - 您可以在AllEmployees变量上方添加以下定义:

std::mutex AllEmpMtx;

然后,在访问或修改AllEmployees的所有函数中,在执行任何此类操作之前锁定互斥锁,如下所示:

std::lock_guard<std::mutex> lock(AllEmpMtx);

例如,在RandomAddEmployees函数中,您应该在lock_guard的调用上方添加AllEmployees.push_back(NewEmployee)

std::lock_guard<std::mutex>实例超出范围时,其析构函数将解锁互斥锁。

顺便说一下,您似乎正在使用EmployeeTypeEmployeePosition的范围枚举。 C ++ 11标准要求使用enum class来定义它们,而不仅仅是enum