我有一个矢量,我想从多个线程访问和使用。我在下面创建了一个示例来说明我想要完成的功能。
目标是(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;
}
答案 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>
实例超出范围时,其析构函数将解锁互斥锁。
顺便说一下,您似乎正在使用EmployeeType
和EmployeePosition
的范围枚举。 C ++ 11标准要求使用enum class
来定义它们,而不仅仅是enum
。