J'ai une struct qui contient deux membres de données: et un vecteur contenant bon nombre de ces objets de structure: p> à Runtime Les membres de données Je ne suis pas sûr que je devrais représenter chaque structure sous forme de Quelle serait la meilleure solution? Devrais-je stocker un vecteur de: p> et lors de l'accès à chaque élément, réinterpret_cast à A, pour obtenir P> P> P> Je voudrais que le verrouillage soit le moins intrusif possible. strong> p> La portabilité n'est pas requise, ce sera sur Linux, 64 bits, X86, GCC 4.8 + compilateur strong> p> p> AB code> et
AC code> à partir d'objets de structure sont définis sur zéro / une valeur
x code>. Toutefois,
B code> et
C code> doit être modifié en même temps - on ne peut pas être mis à jour séparément sans l'autre. Je prévoyais d'utiliser un
compare_exchange_weak () code> pour effectuer la mise à jour. P>
std :: atomic code> dans le vecteur, ou si je devrais placer une union à l'intérieur de la structure, combinez les deux shorts dans un seul
uint32_t code>, puis modifiez ceci: p>
d p> p>
4 Réponses :
Je recommande d'envelopper les variables dans une classe avec un getter et un setter gardé par un mutex et faites les variables privées. P>
L'utilisation d'un syndicat pourrait provoquer une fonctionnalité imprévue basée sur l'architecture de la machine et les drapeaux de compilateur. p>
Modifier STRY> Résultats d'exécution d'un programme simple qui stocke des valeurs du type de structure donné (Linux 32bit, X86): P>
MuTex est trop lourd. Cela doit être très rapide :)
L'avez-vous testé dans votre configuration de l'impact? Un test simple (1000.000 magasins sur une variable du type donné) J'ai reçu: -Le magasin Simple (AKA. Pas de protection) -> ~ 4000 US -MUTED Garded Store -> ~ 12000 US-Automic Store utilisant un syndicat -> ~ 21000 US Testés sur un Linux 32 bits, X86
Étant donné que la portabilité n'est pas un problème, le verrouillage n'est que comme intrusif que vous voudriez que cela soit (prenant "intrusif" pour signifier la boucle CAS ou le verrouillage excessif). P>
Dans votre cas, vous pouvez utiliser le Atomic Bountrins directement. p>
afaik, vous devriez être capable de mélanger des tailles de mots pour fonctionner directement sur B code> et
C code>, ainsi que
A code>. Je n'ai jamais fait ça, donc je ne peux pas dire si cela fonctionnerait de manière fiable sur toutes choses x86. Comme toujours: prototype et test, test, test! P>
std :: atomic
a code> dans un
vecteur code>
Vous pouvez copier un atomic code> à l'aide d'une charge dans son constructeur. Si vous devez faire une "copie par lots" d'un vecteur à un autre, vous pouvez utiliser
REINIERPRET_CAST CODE>, aussi longtemps que vous pouvez garantir que les écrivies sont rinçues avant que toutes les opérations atomiques soient utilisées à nouveau.
Juste une mise à jour des gars-portabilité n'est pas requise. Voir le bas de Q.
Les intégrées que vous connaissez sont les "hérités", ils ont été remplacés par gcc.gnu.org/onlineDocs/gcc/_005f_005Fatomic-Builtins.html
Faire simplement un Union code> d'un type atomique assez grand. C'est ce que j'utilise (l'extrait de code n'est pas parfaitement em> portable, à l'aide de
court code> et
int code> serait sûrement préférable - mais c'est assez bon pour moi tel qu'il est), et cela fonctionne parfaitement bien et de manière fiable depuis ... Pratiquement pour toujours:
union A {
struct {
short b;
short c;
};
std::atomic<int> d;
};
J'aurais dû dire que la portabilité n'est pas requise.
@user: Si vous savez avec certitude quelle taille int code> et
court code> sont sur votre plate-forme et que vous ne prévoyez pas de porter le code à un autre, alors c'est parfaitement parfaitement bien (C'est pourquoi j'utilise
int code>, pas
int32_t code> aussi). Mais même si c'est une contrepartie ou une exigence, les modifications sont minuscules :-)
Damon, comment utiliseriez-vous cela avec comparer_and_exchange? Vous rangez Vector puis invoquerez votre comparez_and_exchange sur D? Vous auriez besoin de charger une version non atomique de D comme argument pour comparer_and_exchange?
Oui, vous lisiez la valeur d'E.G. VEC [i] .d code> (ou
iter-> d code>) dans un ("
attendu "> "), puis faites quelque chose comme
VEC [I] .D.Compare_exchange_weak (attendu, souhaité); code>. Vous pouvez également utiliser la version «forte», mais elle n'a vraiment pas beaucoup d'avantages. Comme il peut échouer dans les deux sens, vous devez vérifier le résultat et la boucle dans tous les cas. Bien que la version "faible" puisse défaillonner, il n'y a pas vraiment une grande différence (mais "mais" faible "peut être plus rapide). Notez également que vous DOIT B> RESTAURER
attendu code> si le PO échoue (il sera écrasé!). Oublier de le faire est une source de douleur commune.
Je suis sûr que std :: atomic
-mcx16 code>. (Voir Cette réponse pour une astuce d'union similaire pour séparément atomique Ou un accès atomique tout aussi entier à une paire de pointeurs (ou d'indicateur pointeur +). Par exemple,
atomique
Notez également que Contrairement à C, en C ++, il n'est pas garanti d'être portable d'écrire un membre de l'Union, puis de lire une autre, il n'est donc que portable des compilateurs où cela va bien (par exemple toute saveur de GNU C et probablement beaucoup d'autres).
@Petercordes: disant "non garanti" est en effet très indulgent. Un programme qui fait appel à un comportement indéfini (avec l'exception de préfixe commune, qui ne s'applique toutefois pas ici). Néanmoins, il s'agit d'une solution pragmatique qui "fonctionne bien" sous les hypothèses évidentes qui sont faites ici, telles que deux courts code> S n'ont aucun rembourrage supplémentaire entre eux et deux
court code> s sont la taille d'un
int code>. Généralement, pour les unions de la seule pod ou des types "principalement de POD" tels qu'un entier atomique, ce type de UB "fonctionne simplement". Cela se bloquerait évidemment et brûlerait étant donné un type qui allouait des ressources.
@DAMON: Les problèmes potentiels dont je parlais sont le même genre que vous pouvez obtenir de violer un aliasing strict (par exemple en lisant le deuxième court code> avec
(réinterpret_cast
courte code> S pourrait arriver éventuellement, mais ne pas être reflété dans une lecture de Le
atomic
Sauf si le matériel que vous ciblez les supports double comparaison-and-swap em> (ce qui n'est probablement pas le cas), je pense que vous n'avez que deux solutions portables: P>
introduisez une serrure de niveau supérieur (mutex ou spinlock en fonction de vos préférences) et transportez toutes les opérations sur fusionne les deux membres en un seul Pour déterminer quelle solution est les points de repère les plus rapides, bien sûr. P>
Si vous connaissez le nombre de
B code> et
C code> dans la portée de la serrure acquise . Un mutex est lourd, mais
std :: atomic_flag code> est sans verrou et très léger, même dans des situations de haut niveau. P> li>
std :: atomic
int code> dans
court code> s via un bit masquing . Notez que cela nécessite
Tailleof (int)> = 2 * Taille de taille (court) code>. Utilisez des types d'entiers de taille fixe si vous devez appliquer cela. P> li>
ol>
struct A code>, vous aurez besoin au moment de la compilation, je vous suggérerais de les mettre dans un
std :: Array code>. Si vous ne le faites pas,
std :: vecteur code> est bien tant que ce numéro reste constant tout au long de la vie du vecteur. Sinon, étant donné que
std :: atomic
struct A code >. p>
J'aurais dû dire que la portabilité n'est pas requise.
La référence du CPP a même un exemple de Spinlock implémenté à l'aide de atomic_flag < / code>.
@Park Que se passe-t-il si je mets structurer un tableau de style C brut?
Si vous connaissez le nombre d'éléments à la compilation, je suggère d'utiliser std :: graphique code> à la place. Si vous ne le faites pas,
std :: vecteur code> est bien tant que ce numéro reste constant tout au long de la vie du vecteur. Sinon, vous devez écrire une copie ou déplacer constructeur.
Je déclare
B code> et
C code> comme privé et écrire une seule fonction qui modifie les deux.
Vous ne pouvez pas vraiment utiliser
std :: atomic <> code> intérieur des conteneurs (atomiques ne sont pas copieux ou mobiles). Si le nombre d'éléments du vecteur est corrigé et connu à l'avance, vous pouvez construire le vecteur avec le bon nombre d'éléments, tant que vous n'essayez jamais d'insérer / effacer des éléments
Pourquoi un
réinterpret_cast code> à partir de
uint32_t code> (qui serait un comportement indéfini) au lieu de stocker le type de syndicat dans le vecteur et de lire / écrire directement les membres directement? Qui repose sur le type punning, mais c'est soutenu par de nombreux compilateurs. Cela ne ferait pas les mises à jour atomiques, vous devez utiliser des opérations atomiques pour cela.
Sont
struct code> s définis sans rembourrage s'ils font partie d'un
Union code>? Sinon, votre proposition semble assez risquée ...
Personnellement, je fusionnerais
B code> et
C code> dans un seul type intégré atomique de la taille correcte. Utiliser des opérations bitwises pour définir diverses parties de ce type. Votre syndicat ne sera pas portable en raison de l'emballage de structure.
@Bathsheba Puis-je aligner les deux membres de 16 bits à des frontières de 2 octets?
Pas de manière portable, non. Si vous sacrifiez la portabilité, vous pouvez aussi bien recourir à l'assemblage et utiliser une serrure de bus.
@Filmor, il n'est pas autorisé à effectuer un remplissage au début d'une structure de mise en page standard, et il est peu probable que le rembourrage entre deux shorts sur une plate-forme commune. A
static_assert code> pourrait être utilisé pour vérifier cela.
Que se passe-t-il lorsque le vecteur
code> redimensionne? Cela fera une copie et aucune atomicité n'est garantie. Il est presque certain que vous n'ayez pas Toujours B> Besoin de la garantie que vous demandez: Quand avez-vous besoin de cette garantie? Qui d'autre accède aux données et comment?