繁体   English   中英

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

[英]Accessing and working with std::vector from multiple threads

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

目标是(1)高速,(2)线程安全,以及(3) 尽可能继续使用vector,因为我的较大项目在各处使用了vector,因此,我希望保持这种状态。

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

下面的下面的示例可以编译,但是由于线程不安全,因此崩溃很快。

关于如何解决以下示例的任何想法,然后可以将其应用于较大的项目?

谢谢:

#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;

}

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

#include <mutex>

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

std::mutex AllEmpMtx;

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM