Статические функции C и потокобезопасность

#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 вокруг него в начале функции.