Подтвердить что ты не робот

Безопасность потоков С++ - чтение карты

Я работаю над программой, которая нуждается в std::map и, в частности, такой, как этот map<string,map<string,int>> - она ​​должна быть чем-то вроде ставок смены банка - первая строка является исходной валютой, а вторая на второй карте желательный, а int - их скорость. Вся эта карта будет только для чтения. Мне все еще нужны мьютексы? Я немного смущен всей безопасностью потока, так как это моя первая большая многопоточная программа.

4b9b3361

Ответ 1

Если вы говорите о стандарте std::map и ни один поток не записывает на него, синхронизация не требуется. Одновременное чтение без записи прекрасно.

Если хотя бы один поток выполняет записи на карте, вам действительно нужна какая-то защита, например, мьютекс.

Помните, что std::map::operator[] считается как запись, поэтому используйте std::map::at (или std::map::find, если ключ не может существовать на карте). Вы можете заставить компилятор защищать вас от случайной записи, ссылаясь только на общую карту через const map&.


Прояснилось, что это имеет место в ОП. Для полноты: обратите внимание, что другие классы могут иметь членов mutable. Для них даже доступ через const& может привести к гонке. Если есть сомнения, проверьте документацию или используйте что-то еще для параллельного программирования.

Ответ 2

Правило большого пальца - это если у вас общие данные, и по крайней мере один поток будет писателем, тогда вам потребуется синхронизация. Если один из потоков является писателем, вы должны иметь синхронизацию, так как вы не хотите, чтобы читатель читал элемент, который записывается. Это может вызвать проблемы, так как читатель может прочитать часть старого значения и часть нового значения.

В вашем случае, поскольку все потоки будут только когда-либо считывать данные, они ничего не могут сделать, что повлияет на карту, чтобы вы могли читать параллельные (несинхронизированные).

Ответ 3

Оберните std::map<std::string, std::map<std::string,int>> const в пользовательский класс, который имеет только const функции-члены [*].

Это гарантирует, что все потоки, которые используют объект класса после его создания, будут только считываться из него, что гарантировано будет безопасным с С++ 11.

Как документация говорит:

Все функции const-члена могут вызываться одновременно разными потоки в одном контейнере.

Упаковка контейнеров в ваших собственных пользовательских типах является хорошей практикой в ​​любом случае. Повышенная безопасность потоков - это лишь один положительный побочный эффект этой хорошей практики. Другие положительные эффекты включают повышенную читаемость кода клиента, сокращение/адаптацию интерфейса контейнера к требуемой функциональности, простоту добавления дополнительных ограничений и проверок.

Вот краткий пример:

class BankChangeRates
{
public:

    BankChangeRates(std::map<std::string, std::map<std::string,int>> const& data) : data(data) {}

    int get(std::string const& key, std::string const& inner_key) const
    {
        auto const find_iter = data.find(key);
        if (find_iter != data.end())
        {
            auto const inner_find_iter = find_iter->second.find(inner_key);
            if (inner_find_iter != find_iter->second.end())
            {
                return inner_find_iter->second;
            }
        }
        // error handling
    }

    int size() const
    {
        return data.size();
    }

private:
    std::map<std::string, std::map<std::string,int>> const data;
};

В любом случае проблема безопасности потока сводится к тому, чтобы убедиться, что конструктор не читает объект, к которому пишет другой поток. Это часто достигается тривиально; например, объект может быть сконструирован до начала многопоточности или может быть инициализирован с помощью жестко закодированных списков инициализации. Во многих других случаях код, который создает объект, обычно будет иметь доступ только к другим потокобезопасным функциям и локальным объектам.

Дело в том, что одновременный доступ к вашему объекту всегда будет безопасным после его создания.


[*] Конечно, функции-члены const должны выполнять свои обещания, а не пытаться "обходные пути" с помощью mutable или const_cast.

Ответ 4

Если вы полностью уверены, что обе карты ВСЕГДА READONLY, тогда вам не нужны мьютексы.

Но вы должны быть очень осторожны, чтобы никто не мог обновлять карту любыми способами во время выполнения программы. Убедитесь, что вы инициализируете карту на этапе инициализации программы, а затем никогда не обновляете ее по какой-либо причине.

Если вы смущены тем, что в будущем вам может потребоваться обновить его между выполнением программы, тогда лучше иметь макросы вокруг карты, которые сейчас пусты. И в будущем, если вам нужны мьютексы вокруг них, просто измените определение макроса.

PS:: Я использовал карту в ответ, которую можно легко заменить на общие ресурсы. Это было для удобства понимания