Получение блокировки путем проверки состояния и повторной проверки

Является ли что-то вроде этого действительным:

std::vector<std::vector<int>> data;
std::shared_mutex m;
...

void Resize() {
    // AreAllVectorsEmpty: std::all_of(data.begin(), data.end(), [](auto& v) { return v.empty(); }
    if (!AreAllVectorsEmpty()) {
        m.lock();
        if (!AreAllVectorsEmpty()) {
            data.resize(new_size);
        }
        m.unlock();
    }
}

я проверяю AreAllVectosEmpty() и затем, если условие выполнено успешно, затем берется блокировка, а затем снова проверяется на то же условие, выполнять ли изменение размера.

Будет ли это потокобезопасным? Resize вызывается только одним потоком, но другие потоки манипулируют элементами data,

Это требование, которое AreAllVectorsEmpty иметь забор памяти или овладеть семантикой?

Редактировать: другие темы будут блокироваться, когда m.lock приобретается Resize,

Изменить: Давайте также предположим, new_size достаточно велик, чтобы произошло перераспределение.

Изменить: Обновить код для shared_mutex.

Редактировать: AreAllVectorsEmtpy перебирает вектор данных. Никто другой не изменяет вектор данных, но данные [0], данные [1] и т. Д. Изменяются другими потоками. Я предполагаю, что переменная размера data[0] находится внутри вектора и представляет собой простое целое число, поэтому безопасно обращаться к data[0].size(), data[1].size() и т. Д. В Resize нить. AreAllVectorsEmpty перебирает data и проверка vector.empty(),

4 ответа

Ответ полностью зависит от того, как AreAllVectorsEmpty реализовано.

Если он просто проверяет флаг, который может быть установлен атомарно, то да, это безопасно. Если он выполняет итерацию по вектору, который вы намереваетесь изменить (или другим часто используемым контейнерам), то нет, это небезопасно (что происходит с итераторами, если вектор выполняет внутреннее перераспределение???).

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

Затем вы получите общую блокировку перед проверкой, а в случае модификации - эксклюзивную блокировку.

Знать, что если areAllVectorsEmpty использует некоторую независимую структуру данных (кроме упомянутого атомарного флага), вам, возможно, придется защищать ее с помощью отдельного мьютекса.

Я хотел бы использовать shared_mutex и использовать:

  • общая блокировка во всех потоках, которые просто читают вектор (при чтении вектора)
  • уникальный замок в этой теме при изменении размера вектора

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

Блокировка автоматически подразумевает барьер памяти, иначе блокировка не имела бы особого смысла.

Стандарт не требует, чтобы это работало, сравните http://en.cppreference.com/w/cpp/container. Если это работает с вашим конкретным компилятором и STL? Вам нужно будет заглянуть в источники. Но я бы на это не рассчитывал.

Это подводит меня к вопросу: почему вы хотите это сделать? По причинам производительности? Вы измерили производительность? Действительно ли это измеримое снижение производительности при блокировке перед вызовом? AreAllVectorsEmpty?

Кстати, пожалуйста, не блокируйте мьютекс напрямую, используйте std:: lock_guard.

// AreAllVectorsEmpty: std::all_of(data.begin(), data.end(), [](auto& v) { return v.empty(); }

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

Другие вопросы по тегам