Comment puis-je utiliser quelque chose comme std :: vector ?

J’ai un nombre important, mais potentiellement variable, d’objects dans lesquels sont écrits simultanément. Je veux protéger cet access avec des mutex. À cette fin, je pensais utiliser std::vector , mais cela ne fonctionne pas, car std::mutex n’a pas de constructeur de copie ou de déplacement, alors que std::vector::resize() nécessite cette.

Quelle est la solution recommandée à cette énigme?

edit : tous les conteneurs à access aléatoire C ++ nécessitent-ils une copie ou un déplacement des constructeurs pour le redimensionnement? Est-ce que std :: deque pourrait vous aider?

éditer à nouveau

Tout d’abord, merci pour toutes vos pensées. Je ne suis pas intéressé par les solutions qui évitent les mutices et / ou les déplacent dans les objects (je m’abstiens de donner des détails / raisons). Donc, étant donné le problème que je veux un nombre ajustable de mutices (où l’ajustement est garanti quand aucun mutex n’est verrouillé), il semble y avoir plusieurs solutions.

1 Je pourrais utiliser un nombre fixe de mutices et utiliser une fonction de hachage pour mapper des objects aux mutices (comme dans la réponse du capitaine Oblivous). Cela entraînera des collisions, mais le nombre de collisions doit être faible si le nombre de mutices est beaucoup plus grand que le nombre de threads, mais rest inférieur au nombre d’objects.

2 Je pourrais définir une classe wrapper (comme dans la réponse de ComicSansMS), par exemple

 struct mutex_wrapper : std::mutex { mutex_wrapper() = default; mutex_wrapper(mutex_wrapper const&) noexcept : std::mutex() {} bool operator==(mutex_wrapper const&other) noexcept { return this==&other; } }; 

et utilisez un std::vector .

3 Je pourrais utiliser std::unique_ptr pour gérer des mutex individuels (comme dans la réponse de Matthias). Le problème avec cette approche est que chaque mutex est alloué et désalloué individuellement sur le tas. Donc je préfère

4 std::unique_ptr mutices( new std::mutex[n_mutex] );

quand un certain nombre n_mutex de mutices est alloué initialement. Si ce nombre était jugé insuffisant par la suite, je me contenterais

 if(need_mutex > n_mutex) { mutices.reset( new std::mutex[need_mutex] ); n_mutex = need_mutex; } 

Alors, lequel de ces (1,2,4) devrais-je utiliser?

vector exige que les valeurs soient déplaçables, afin de conserver un tableau contigu de valeurs au fur et à mesure de sa croissance. Vous pouvez créer un vecteur contenant des mutex, mais vous ne pouvez rien faire qui puisse nécessiter de le redimensionner.

Les autres conteneurs ne sont pas soumis à cette exigence. soit la liste deque ou la [forward_]list devrait fonctionner, tant que vous construisez les mutex sur place soit pendant la construction, soit en utilisant emplace() ou resize() . Des fonctions telles que insert() et push_back() ne fonctionneront pas.

Vous pouvez également append un niveau supplémentaire d’indirection et stocker unique_ptr ; mais votre commentaire dans une autre réponse indique que vous estimez que le coût supplémentaire de l’allocation dynamic est inacceptable.

Vous pouvez utiliser std::unique_ptr au lieu de std::mutex . unique_ptr s sont mobiles.

Si vous voulez créer une certaine longueur:

 std::vector mutexes; ... size_t count = 4; std::vector list(count); mutexes.swap(list); 

Je suggère d’utiliser un pool de mutex fixe. Conservez un tableau fixe de std::mutex et sélectionnez celui que vous souhaitez verrouiller en fonction de l’adresse de l’object, comme vous le feriez avec une table de hachage.

 std::array mutexes; std::mutex &m = mutexes[hashof(objectPtr) % mutexes.size()]; m.lock(); 

La fonction hashof pourrait être quelque chose de simple qui décale la valeur du pointeur de quelques bits. De cette façon, il vous suffit d’initialiser les mutex une seule fois et vous évitez la copie du redimensionnement du vecteur.

Si l’efficacité pose un tel problème, je suppose que vous n’avez que de très petites structures de données qui sont modifiées très souvent. Il est alors probablement préférable d’utiliser Atomic Compare And Swap (et d’autres opérations atomiques) au lieu d’utiliser des mutex, plus précisément std::atomic_compare_exchange_strong