поток unordered_set безопасен, если гонка данных не имеет значения? - PullRequest
1 голос
/ 28 января 2020

Я хотел бы, чтобы потоки добавляли уникальные числа с плавающей точкой в ​​список, и сначала подумал о приближении к нему, вставив в unordered_map<float,int>, где значения не имеют значения (по сути, это unordered_set<float>). Потоки могут перекрывать друг друга значениями 1 в том же ключе, ie. не имеет значения, если поток 1 сначала записал значение 1 или поток 2, потому что в конце он просто будет равен 1. Таким образом, гонки данных должны быть неактуальными.

#include <iostream>
#include <string>
#include <unordered_set>
#include <thread>
#include <set>

std::unordered_set<float> mySet;

void someTask(int i) {
    mySet.insert(i % 3);
}

int main()
{
    std::thread threads[8];
    for (int i = 0; i < 8; i++) {  
     threads[i] = std::thread(someTask, i);
    }
    for (int i = 0; i < 8; i++) {  
     threads[i].join();
    }    

    for (auto const& mySetVal : mySet) {
     std::cout << mySetVal << std::endl;    // Oddly, mySet might have duplicates
    }    

    std::set<float> mySet_( mySet.begin(), mySet.end() ); // Make a new ordered set, and removing duplicates along the way

    for (auto const& mySetVal : mySet_) {
     std::cout << mySetVal << std::endl;   // Would this always produce the expected result of 0,1,2?
    }
}

Однако mySet, несмотря на то, что он является unordered_set, имеет дубликаты, и я предполагаю, что это связано с состоянием гонки во время чтения для ключа, и в итоге мы вставили дважды? Но когда я заменяю mySet на unordered_map<float,int>, все равно остаются дубликаты - я бы подумал, даже если есть условие гонки, и хотя мы не можем гарантировать, какой поток будет выполняться первым, в конце потоки могут переопределить друг друга без какого-либо вреда.

Так почему же есть дубликаты?

И, удалив дубликаты в конце, это потокобезопасно, или это даст ожидаемые результаты 0,1 , 2 последовательно / надежно? Если нет, то поточно-ориентированное решение для кода выше было бы здорово!

1 Ответ

3 голосов
/ 28 января 2020

Все стандартные контейнеры безопасны для чтения одновременно, и вы можете одновременно изменять различные элементы одного и того же контейнера, но не одного и того же элемента: https://en.cppreference.com/w/cpp/container#Thread_safety

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...