6
votes

Le mot clé mutable C ++ est-il mauvais pour les fils?

De ma compréhension du mot clé mutable, une de ses utilisations principales est la mise en cache des données et l'informer en cas de besoin. Depuis qu'ils peuvent changer (même s'ils sont constants) ne serait-il pas dangereux ou inutile de les utiliser? La partie de mise en cache modifie les données afin qu'il y aurait besoin d'une serrure et de ma compréhension lorsque vous écrivez pour des multithreads, les données ne doivent jamais modifier et les copies doivent être effectuées et retournées / chaînées ensemble.

Il est donc inutile ou mauvais d'utiliser le mot-clé mutable C ++?


11 commentaires

Je dirais que vous mettriez mal compris le mot clé mutable . Où avez-vous eu cette idée qu'il est destiné à affecter la mise en cache de données?


@Jonathan: la mise en cache des données est un usage très courant des membres de données mutable. Si vous avez une fonction de membre de const qui effectue une tâche coûteuse et que vous savez que la fonction est appelée fréquemment, une optimisation typique consiste à introduire un élément de données mutable pour mettre en cache les résultats de la tâche coûteuse afin que les appels futurs sur cette fonction de membre soient plus rapides. .


Oui, mais c'est un cas d'utilisation, pas la signification du mot clé.


@Jonathan: L'OP n'a pas dit que c'était la signification du mot clé; Il a dit que c'était l'utilisation principale du mot clé (il serait mieux formulé, peut-être, s'il l'a dit "une de ses principales utilisations est ..." Au lieu de "Son utilisation principale est ...").


Regardez, l'OP est confus. Le mot clé mutable est totalement orthogonal pour enfiler. Vraiment, mutable n'a rien de plus à voir avec le filetage que const fait. Toutes les modifications aux données partagées entre des threads, quelles que soient si les données sont mutables ou non, doivent être synchronisées. James McNellis a souligné un cas possible où le mot clé mutable dans un programme fileté pourrait conduire à un faux sentiment de sécurité, mais cela justifie à peine d'associer les deux concepts car ce n'est pas comme si le const < / Code> Le mot-clé (ou quoi que ce soit d'autre en C ++ 03) rend toute Garanties sur la sécurité thread-sécurité.


@Chals Salvia: J'ai clairement dit que les données seraient modifiées et doivent être synchronisées. Theres aucune confusion sur ma part


@ acidzombie24: ... mais vous avez impliqué que const est lié à la multithreading que ce n'est pas le cas. Il n'est pas sûr d'appeler une méthode const dans un environnement multithreadé sans serrures si un autre thread peut appeler une méthode non const sur le même objet. De ce point de vue, mutable est aussi non liée à multithreading que const est. D'autre part, il est considéré comme prudent pour appeler des méthodes qui ne modifient pas l'objet de plusieurs threads (à condition qu'il existe une garantie selon laquelle aucun autre thread ne modifie l'objet), mais ce n'est pas la même chose que d'appeler un Const La méthode est sûre.


@David Rodríguez - Dribesas: Oui Exactement. Mais beaucoup assumeraient quand il est constant que cela ne modifierait pas la classe (ou du moins de manière non sûre). Ainsi, mon point, est-il «mauvais» d'utiliser le mot clé mutable. Je voudrais simplement écrire la classe pour ne pas l'utiliser afin d'obtenir une meilleure performance. Je demande vraiment que s'il y a un cas où il serait logique d'utiliser le mot clé mutable, car dans la plupart des cas, vous éviteriez la possibilité de modifier les données si possible.


@ acidzombie24: lors de l'écriture de code multithreadisé, plus qu'avec tout autre type de programme, le conseil serait le faire droit alors faites-le rapide seulement après la mesure et si nécessaire . Ne pas verrouiller peut être une solution pour un problème particulier, mais dans le cas général, il y a de fortes chances que vous vous sacidiez et que vous avez un problème vraiment difficile à essayer de déboguer le code plus tard. Le code sans verrouille nécessite beaucoup d'attention, et cela signifie en réalité savoir ce que chaque fonction fait réellement ou ne le fait pas.


@ acidzombie24: Avez-vous besoin de verrouiller lorsque vous appelez une méthode int foo () const pour obtenir un résultat constant s'il n'y a pas d'attributs mutables dans l'objet? Cela dépend de FOO , dans le meilleur cas, le int est uniquement lu à partir de l'objet, et c'est un mot aligné et vous pouvez le tirer sans verrouillage. Dans le pire des cas, le résultat réel est calculé (sans rien modifier) ​​de plus d'un attribut ou n'est pas correctement aligné, aucun de ces cas rendrait le déverrouillage d'appel dangereux. Le problème n'est pas le mot clé mutable , mais what la fonction fait et comment


En fait, la bibliothèque standard n'utilise jamais aucun membre mutable , car la standard garantit que tous les membres de la fonction const sont thread-coffre-fort.


3 Réponses :


2
votes

Non, le mot clé mutable est de sorte que vous puissiez avoir des champs à l'intérieur d'un objet pouvant changer même lorsque l'objet est constitué, tel que pour les métadonnées qui ne font pas partie des propriétés d'un objet, mais sa gestion (comme des comptoirs, etc. .). Il n'a rien à voir avec le filetage.


14 commentaires

Le filetage est un problème ici. Si vous avez un certain nombre de threads, tous avec des références de const à un objet partagé, il est facile de supposer que tous ces threads peuvent appeler les fonctions de membre constant sur cet objet sans aucun type de synchronisation. Toutefois, s'il existe des éléments de données mutables modifiés par ces fonctions membres de Const, vous devez vous assurer que ces modifications sont correctement synchronisées.


Ce que je voulais dire, c'est que le mot clé lui-même n'a rien à voir avec le filetage, la manière dont une personne comme volatile fait. Et je jamais suppose que les méthodes d'appel sur un objet Const de deux threads fonctionne - non pas à cause de cela, mais en raison du fait que, tandis que, tandis que const signifie que l'objet ne signifie que l'objet 't Change, cela ne dit rien des objectifs de toutes les cibles ou des références qu'il pourrait avoir - afin que vous puissiez modifier quelque chose de mal à l'aide de références constantes sans données mutables.


Vous avez tort de mutable, il permet à Membres Vars marqués mutable d'être modifié dans des méthodes marquées Const, peu importe si l'instance qu'elles soient appelées est constituée ou non.


Hein? N'est-ce pas exactement ce que j'ai écrit dans mon post? ("Le mot clé mutable est de sorte que vous puissiez avoir des champs à l'intérieur d'un objet qui peut changer même lorsque l'objet est constitué ...")


Certainement, le problème est un problème conceptuel: il est assez facile de raisonner que (i) tous mes threads n'appellent que les fonctions de membre Const sur cet objet partagé, (ii) des fonctions membres de Conscons ne modifient pas l'objet sur lequel ils sont appelés, (III ) Si un objet n'est pas modifié, je n'ai pas besoin de synchroniser l'accès à celui-ci, donc (IIII), je n'ai pas besoin de synchroniser l'accès à l'objet. Cet argument est faux parce que (ii) est faux, mais c'est vraiment facile de penser que cet argument a raison.


@James: Vous avez raison que (ii) est faux, mais mon point est, ce n'est même pas à cause de cette raison. Même si mutable n'existait pas, et même si les champs de l'objet ne changent jamais, ce qu'ils point peuvent changer, et donc il n'est donc pas sûr d'utiliser Const pour multithreading, même si mutable n'a même pas existé.


(En outre, je ne me souviens pas si vous pouvez avoir des références const à des objets mutables ou non, mais je pense toujours à const en tant que const Voir d'un objet potentiellement mutable, pas un objet constant, pour les raisons ci-dessus, que ce soit ou non mutable existe.)


@Lambert: Oui, vous pouvez avoir une référence constante à un objet non-Const. Si je comprends bien votre commentaire correctement, vous affirmez que c'est une question plus fondamentale et que lorsque vous utilisez un objet de plusieurs threads, vous avez vraiment besoin d'une bonne compréhension de la garantie de la concurrence que cela garantit. Je suis d'accord avec ça, 100%. Je suppose que mon argument est plus difficile de ", il est potentiellement plus facile pour un utilisateur de la classe à bousiller s'il y a un état mutable caché, non synchronisé".


Exemple: si vous avez un écrivain de fichier const , vous ne pouvez pas supposer qu'il est synchronisé simplement car chaque champ (Poignée d'exploitation, etc.) est const , même s'il jamais changements - simplement parce que vous n'avez aucune idée de savoir si le pointe de la poignée est également constitué. (@James: Modifier, sur votre commentaire: Oui, c'est une question beaucoup plus fondamentale, et jusqu'à aujourd'hui, je n'ai même jamais vu les mots mutables ou const dans la même phrase Comme synchroniser ... Ils sont complètement différents et ne garantissent aucune garantie d'une autre, réelle ou imaginaire.)


-1. Pas parce que vous avez mal (votre droit, du moins dans ces commentaires). Mais je n'ai pas demandé si un mot clé mutable a quelque chose à voir avec le filetage. Juste si c'est mauvais de les utiliser avec une filetage.


@Lambert: Vous avez dit "même lorsque l'objet est Const" j'ai dit "même lorsque la méthode est constituée si l'objet est constitué ou non ". Légère distinction, même si le ce pointeur est Const à l'intérieur d'une méthode du const.


Disons également que STL String Class utilise le mot clé mutable lors de l'appel C_STR (). Vous vous attendez à ce que II soit vrai parce que ce n'est pas quelque chose d'évident comme un tableau de const pointant vers (non correspondance). Vous pensez que sa chaîne constante ne change jamais.


+1 Parce que je suis d'accord avec votre raisonnement que c'est une question plus fondamentale. C'est juste un problème malheureux que de nombreuses personnes ne soient pas extrêmement prudentes lors de la rédaction de code multithreadé (bien sûr, je sais que j'ai fixé des bugs dans mon propre code aussi où je n'étais pas aussi prudent que j'aurais dû être).


@ ACIDEZOMBIE24: Eh bien, la réponse n'était pas "oui" ou "non" parce que vous semblez avoir mal compris le point de "mutable". Il est tout simplement sans rapport avec enfilage, cela n'affecte pas la bonne et la mauvaise ness du code. @Eugen: Je ne vois toujours rien de mal, même subtilement. Pourriez-vous s'il vous plaît clarifier ce qui est incorrect / incomplet sur ce que j'ai dit? @ ACIDEZOMBIE24: Le problème est que vous n'avez jamais jamais appelez une méthode sur un objet de deux threads à la fois, qu'il s'agisse ou non d'une constante, et de quoi ou non mutable existe.



18
votes

Il est donc inutile ou mauvais d'utiliser le mot-clé mutable C ++?

non; Le mot clé mutable est une bonne chose. mutable peut être utilisé pour séparer l'état observable d'un objet du contenu interne de l'objet.

avec l'exemple "Données en cache" que vous décrivez (une utilisation très courante de mutable ), il permet à la classe d'effectuer des optimisations "sous les couvertures" qui ne modifient pas réellement l'état observable .

En ce qui concerne l'accès à un objet de plusieurs threads, oui, vous devez faire attention. En général, si une classe est conçue pour être accessible à partir de plusieurs threads et il dispose de variables mutable , il doit synchroniser la modification de ces variables en interne. Notez cependant que le problème est vraiment plus conceptuel. Il est facile de raisonner que:

  1. Tous mes threads n'appellent que les fonctions de membre constant sur cet objet partagé
  2. Les fonctions de membre constant ne modifient pas l'objet sur lequel ils sont appelés
  3. Si un objet n'est pas modifié, je n'ai pas besoin de synchroniser l'accès à celui-ci
  4. Par conséquent, je n'ai pas besoin de synchroniser l'accès à cet objet

    Cet argument est faux parce que (2) est faux: les fonctions membres de la const peuvent en effet modifier des membres de données mutables. Le problème est que c'est vraiment, vraiment facile de penser que cet argument a raison.

    La solution à ce problème n'est pas facile: efficacement, il vous suffit d'être extrêmement prudent lors de la rédaction de code multithread et d'être absolument certain que vous comprenez soit comment les objets partagés entre les threads sont mis en œuvre ou quelles sont les garanties de la concurrence. < / p>


12 commentaires

Si une classe est conçue pour être accessible à partir de plusieurs threads et que toutes les variables non constant, il doit synchroniser la modification de ces variables.


@CHARDLES: Eh bien, je pense que cela peut être affirmé que c'est plus un problème avec les membres de données mutables. Si vous avez un certain nombre de threads avec des références de const à un objet partagé, il est souvent raisonnable de supposer que vous pouvez appeler ces fonctions de membre de const sans aucune synchronisation. Il est facile d'oublier mutable et de penser de manière incorrecte que les fonctions des membres de Conscons ne peuvent pas du tout muté l'état sous-jacent.


@James: une règle que je suive toujours: si vos données immuables sont simplement que - immuables Data - alors votre "objet" n'avait plus de méthodes, et il vaut mieux avoir des membres qui sont mieux Tous Const et qui pointent transitivement vers des membres immuables. Si vous pouvez satisfaire ces conditions, n'hésitez pas à accéder à l'objet «Objet» (AKA Agrégate de données) sans serrures. Si votre code appelle toutes les méthodes sans serrures, le code est dangereux, quelles que soient les conditions.


@Lambert: Je pense que c'est un peu extrême. Par exemple, si vous avez une bibliothèque de conteneurs compatibles C ++ 0x, vous êtes garanti que vous pouvez itérer sur le conteneur simultanément tant que vous n'effectuez aucune opération invalidation des itérateurs (il pourrait y avoir plus de restrictions que cela; i ' M Non certain à 100% sur toutes les améliorations de la concurrence à la bibliothèque standard, mais c'est ce que je crois comprendre. J'aurais certainement davantage de recherches si je voulais compter sur cela.) Une classe peut fournir des guranties explicites sur sa convivialité simultanée.


@James: D'accord, c'est un peu extrême, car il y a une petite exception: "Sauf lorsque la classe explicitement garantit la sécurité du fil". Mais prenez cela avec prudence - I Seul Utilisez cette affirmation d'objets de wrapper dont le seul but est de fournir une sécurité de thread, car il n'y a pas à peu près tout à fait de faire de l'autre objet Thread-Safe-Safe - Plusieurs procédés sont presque toujours dangereux, même si le conteneur sous-jacent est sûr. (Par exemple: ajoutez un objet, puis retirez-le - même si chacun de ceux-ci est synchronisé, ils ne sont pas synchronisés ensemble, alors pourquoi verrouiller le conteneur à la fois interne et extérieurement?)


@James: Une fois encore je suis d'accord, bien que je sois encore plus précis. Même sans mutable, car const n'est pas propagé par des pointeurs (malheureusement?) const méthodes sur un objet peut réellement modifier son état. Que ce soit bon ou mauvais est argoutable, ce qui est certain que la langue le permettait et qu'il est laissé au développeur de bien réussir.


@Matthieu: Exactement - c'est pourquoi je considère que "const" est comme une limitation "autorisation" pour le spectateur plutôt qu'un contrat contraignant pour l'objet. Pour moi, cela signifie "vous ne pouvez pas écrire ici" plutôt que "ces données sont immuables".


@Lambert: En fait, il y a 2 utilisations. Certains utilisateurs utiliseront Const comme liaison (Constance physique), tandis que d'autres (comme moi) le préfèrent exprimer une conscience logique (sur l'état observable de l'objet) avec le raisonnement que l'état non observable (comme la mise en cache) est une implémentation de détail. Pour chacun ses sons, je suppose, mais mieux ne pas mélanger les deux concepts dans une base de code :)


@Matthieu: haha ​​je vois ce que tu veux dire, mais je suppose que mon interprétation est le choix 3: c'est un drapeau de permission en lecture seule; Il ne dit rien sur la manière dont l'objet se comporte, à moins que ce soit de la POD sans pointers intégrés (et par conséquent, à moins que cela n'a pas de méthodes).


À propos, vous pourriez trouver Ce lien à propos de const contre immutable dans d intéressant.


@Lambert: BTW sur les méthodes et la pod. Certaines langues ont des méthodes sur Enums. Quelle est la plus étrange? J'ai cependant pensé que c'était bizarre puis trouvé une utilisation pour cela. Quoi qu'il en soit, je lis que D lien d maintenant et je pense que la pod et les classes doivent être considérés comme des choses différentes et avoir un nom différent. En règle générale, j'appelle des structures comme pod et classes comme des choses que je n'ai pas vraiment de grénage;)


@ acidzombie24: Ouais, les méthodes d'enum sont une sorte de bizarre, comme en Java (et peut-être de D, je ne suis pas sûr) ... Je peux les surmonter, car je n'ai pas encore vue une sorte de mutation sur n'importe quoi. Et oui, la POD n'est pas un très grand terme - en fait, je n'aime pas cela en C ++, les classes ne sont que des structures privées. Dans d'autres langues, il y a beaucoup de différences claires (par exemple c #, d) et uniquement des structures peuvent être une pod, pas des classes (bien que toute évidence, toutes les structures ne soient pas pod). Et j'aime votre dernière phrase, bien que je faire avez des matrices d'objets. :)



8
votes

à l'extrémité opposée, la plupart de mon code multithreaded nécessite l'utilisation du mot clé mutable : xxx

Le fait que le fait que le Obtenir la méthode ne modifie pas l'état de l'objet est déclaré au moyen du mot clé const . Mais sans le modificateur MAUTITABLE appliqué à la déclaration de l'attribut mutex , le code ne pourrait pas verrouiller ni libérer les opérations Mutex --Both, clairement modifier < / em> le mutex , même s'ils ne modifient pas l'objet .

Vous pouvez même créer l'attribut data mutable si cela peut être évalué paresseusement et que le coût est élevé, tant que vous verrouillez l'objet. Ceci est l'utilisation cache que vous vous référez à la question.

Le modificateur mutable mutable n'est pas un problème avec le code multithreadisé, uniquement lorsque vous essayez de Faites Lock-moins multithreading. Et comme avec tout programmation Lock-moins , vous devez être très prudent avec ce que vous faites, quel que soit le const ou mutable . Vous pouvez écrire un code multithreadisé parfaitement dangereux qui appelle les méthodes const sur des objets sans attributs mutable . L'exemple simple retirerait le mutex du code précédent et que n threads n'effectue que get () s tandis qu'un autre thread exécute Set () s. Le fait que get () est const n'est pas une garantie que vous ne recevrez pas de résultats invalides si un autre thread modifie.


7 commentaires

Ne serait-il pas aussi sûr de ne pas verrouiller obtenir ()? La seule façon de voir que m_data étant invalide est si M_DATA se trouve être> 32 ou 64 bits et il doit effectuer plusieurs missions pour copier l'objet complet. Est-ce la seule raison pour laquelle? Si m_data était un pointeur, je ne vois aucune raison de verrouiller.


@ ACIDEZOMBIE24: Tout dépend de l'architecture avec laquelle vous travaillez et un groupe d'autres facteurs, l'atomicité des lectures / écritures n'est pas appliquée par la langue. Si type est une structure contenant 4 caractères, la lecture ne sera probablement pas atomique. Même s'il s'agit d'une valeur unique 32 bits dans un processeur Intel moderne, on peut être non atomique si les données (dans ce cas objet ) ne sont pas alignées. Encore une fois, lors de l'écriture de code sans verrouillage, vous devez prendre en compte beaucoup .


Pour votre exemple spécifique, je recommanderai un verrouillage de lecture de lecture lien


@Niki: Il y a une surcharge sur les serrures en lecture, et il est livré avec son propre ensemble de problèmes. Si la section critique est petite, ou le nombre de lecteurs / lectures n'est pas extrêmement supérieur au nombre d'écritures (comme dans plusieurs commandes de magnitude plus grande), vous pouvez être meilleur avec un mutex ordinaire.


@David Rodríguez Enfin, tout dépend de la fréquence de l'utilisation de la classe, quel est le type et combien de temps il faut pour obtenir () à exécuter. Rwlock est toujours plus lourde, mais peut également être mis en œuvre de manière à devenir lourds lors de la conflit lors de l'écriture. Mais je suis sûr que vous savez beaucoup tout ça. Droit d'outils pour le bon problème.


@Niki: Dans mon expérience personnelle, et cela est complètement biaisé, je n'ai jamais rencontré un cas où Rwlock était la meilleure solution. J'ai eu des cas où c'était mieux, mais après avoir travaillé sur la réduction des tailles de la section critique, le mutex ordinaire a fini par être la meilleure solution. Mesure, votre kilométrage peut varier. Mais j'ai supprimé une juste quantité de Rwlocks dans le codebase que je travaille pour de bonnes raisons. C'est mon expérience personnelle, Rwlocks son génial, mais ils ne sont pas que génial.


@David Rodríguez Merci d'avoir partagé votre expérience et d'être honnête, je n'utilise presque pas Rwlock dans mon code non plus et que pour l'accès à la base de données construit en interne, où la plupart du temps est nécessaire et étouffait trop à cause des serrures normales. Néanmoins, les «bons outils du bon travail» sont toujours à la règle de suivre, avec «simple est belle»