#c #static #thread-safety #stdmap
#c #статические #Потокобезопасность #stdmap
Вопрос:
Хорошо, я искал во всемогущем Google какой-нибудь четкий ответ, который соответствовал бы моей проблеме, но безуспешно. Я разрабатываю уровень аппаратной абстракции на C , который взаимодействует через различные последовательные протоколы (SPI, I2C, UART), и у меня на плате много микросхем, которые нуждаются в управлении. Каждая микросхема получает свой собственный класс. Поскольку я моделирую аппаратное обеспечение с помощью классов, я думаю, что для меня важно иметь такое же количество экземпляров в моем коде, как и количество микросхем, установленных на плате. Вот моя проблема: мне нужно контролировать создание этих экземпляров. Решение, которое я придумал, состояло в том, чтобы хранить значения в статическом std::map, который имеет std::string в качестве ключа (например, я использую имя устройства для SPI и адрес для I2C). Код выглядит примерно так:
IC.h
class SomeICMap {
private:
static std::map<std::string addr, std::shared_ptr<IC> > instance_map;
public:
static IC* getInitializedInstance(std::shared_ptr<CommInterface> comm, const std::stringamp; addr);
}
IC.cpp
std::map<std::string addr, std::shared_ptr<IC> > instance_map;
IC* SomeICMap::getInitializedInstance(std::shared_ptr<CommInterface> comm, const std::stringamp; addr) {
std::map<string, std::shared_ptr<IC> >::iterator it;
it = instance_map.find(addr);
if (it == instance_map.end()) {
std::shared_ptr<IC> device(new IC(comm, addr));
if (device->init() != 0) {
return NULL;
}
instance_map[addr] = device;
return device.get();
}
return it->second.get();
}
Таким образом, я не получаю дублированные экземпляры оборудования, установленного на плате. Я сделал это для каждой микросхемы.
Мой вопрос: является ли это потокобезопасным?
Я собираюсь использовать некоторые из этих микросхем в нескольких потоках, работающих под управлением Linux. Я не уверен, что это будет безопасно, поскольку я получаю доступ к статической карте, чтобы получить указатели и получить доступ к оборудованию. Из того, что я прочитал в Интернете, фактический доступ к оборудованию безопасен, поскольку ядро заботится о параллелизме при использовании write() read() для управления открытым файловым дескриптором. Меня беспокоят условия гонки, возникающие при первом создании программой экземпляров IC.
Комментарии:
1. контейнеры stl не являются потокобезопасными.
Ответ №1:
SomeICMap::getInitializedInstance
не является потокобезопасным: какой-то поток может получить доступ instance_map
к нему, пока другой поток его модифицирует. Это гонка данных, и C не определяет поведение для программ с гонками данных. Вы могли бы решить эту проблему, добавив мьютекс в класс и убедившись, что все обращения к карте выполняются при удержании мьютекса:
class SomeICMap {
private:
static std::map<std::string addr, std::shared_ptr<IC> > instance_map;
static std::mutex mtx; // <-------
public:
static IC* getInitializedInstance(std::shared_ptr<CommInterface> comm,
const std::stringamp; addr);
};
std::map<std::string addr, std::shared_ptr<IC> > SomeICMap::instance_map;
std::mutex SomeICMap::mtx; // <-------
IC* SomeICMap::getInitializedInstance(std::shared_ptr<CommInterface> comm,
const std::stringamp; addr) {
std::lock_guard<std::mutex> lock(mtx); // <-------
std::map<string, std::shared_ptr<IC> >::iterator it;
it = instance_map.find(addr);
if (it == instance_map.end()) {
std::shared_ptr<IC> device(new IC(comm, addr));
if (device->init() != 0) {
return NULL;
}
instance_map[addr] = device;
return device.get();
}
return it->second.get();
}
Комментарии:
1. лучше блокировка чтения-записи, потому что творений мало и далеко друг от друга.
Ответ №2:
Если вы вызываете getInitializedInstance
из более чем одного потока одновременно, то нет, это не потокобезопасно. Причина в том, что map
вставка могла бы чередоваться либо с другой вставкой, либо с чтением.
Простым решением было бы добавить статическое std::mutex
значение в класс и построить a unique_lock
вокруг него в начале функции.