Je suggère que si l'on veut mettre en œuvre copie sur écriture efficace (pour les chaînes ou autre), on doit définir un type d'emballage qui se comporte comme une chaîne mutable, et qui tiendra à la fois un nullable référence à une chaîne mutable (aucune autre référence à cet élément n'existera jamais) et une référence nullable à une chaîne "immuable" (références à ce qui n'existera jamais en dehors des choses qui ne tenteront pas de le faire muter). Les wrappers seront toujours créés avec au moins une de ces références non nulles; Une fois que la référence de l'élément modifiable est définie sur une valeur non nulle (pendant ou après la construction), elle se rapportera toujours à la même cible. Chaque fois que les deux références sont non nulles, la référence immuable pointera vers une copie de l'article qui a été faite quelque temps après la mutation complète la plus récente (lors d'une mutation, la référence immuable peut ou non contenir une référence à une valeur de pré-mutation).
Pour lire un objet, vérifiez si la référence "mutable-item" est non nulle. Si oui, utilisez-le. Sinon, vérifiez si la référence "immutable-item" est non nulle. Si oui, utilisez-le. Sinon, utilisez la référence "mutable item" (qui sera désormais non nulle).
Pour muter un objet, vérifiez si la référence "mutable-item" est non nulle. Sinon, copiez la cible de la référence "élément immuable" et CompareExchange une référence au nouvel objet dans la référence "élément modifiable". Ensuite, muter la cible de la référence "mutable item" et invalider la référence "immutable item".
Pour cloner un objet, si le clone doit être cloné à nouveau avant d'être muté, récupérez la valeur de la référence "immutable-item". Si c'est null, faites une copie de la cible "mutable item" et CompareExchange une référence à ce nouvel objet dans la référence de l'élément immuable. Créez ensuite un nouveau wrapper dont la référence "mutable-item" est null, et dont la référence "immutable-item" est soit la valeur récupérée (si elle n'était pas nulle) ou le nouvel élément (si c'était le cas).
Pour cloner un objet, si le clone doit être muté avant d'être cloné, récupérez la valeur de la référence "immutable-item". Si null, récupérez la référence "mutable-item". Copiez la cible de la référence récupérée et créez un nouveau wrapper dont la référence "mutable-item" pointe vers la nouvelle copie et dont la référence "immutable-item" est null.
Les deux méthodes de clonage seront sémantiquement identiques, mais choisir la mauvaise pour une situation donnée entraînera une opération de copie supplémentaire. Si l'on choisit systématiquement l'opération de copie correcte, on obtiendra la plupart des avantages d'une approche "agressive" de copie sur écriture, mais avec beaucoup moins de frais généraux. Chaque objet de stockage de données (par exemple une chaîne de caractères) sera soit mutable sans partage, soit partagé de manière immuable, et aucun objet ne basculera jamais entre ces états.Par conséquent, on peut, si on le souhaite, éliminer tout "surcharge de threading/synchronisation" (en remplaçant les opérations CompareExchange par des mémoires directes) à condition qu'aucun objet wrapper ne soit utilisé simultanément dans plus d'un thread. Deux objets wrapper peuvent contenir des références au même détenteur de données immuable, mais ils pourraient être inconscients de l'existence des uns et des autres.
Notez que quelques opérations de copie supplémentaires peuvent être requises lors de l'utilisation de cette approche que lorsque vous utilisez une approche "agressive". Par exemple, si un nouveau wrapper est créé avec une nouvelle chaîne et que ce wrapper est muté et copié six fois, le wrapper d'origine contiendra des références au porteur de chaîne d'origine et une référence immuable contenant une copie des données. Les six enveloppes copiées contiendraient juste une référence à la chaîne immuable (deux chaînes au total, bien que si la chaîne originale n'était jamais mutée après que la copie ait été faite, une implémentation agressive pourrait s'en sortir avec un). Si le wrapper d'origine était muté, avec cinq des six copies, alors toutes les références à la chaîne immuable sauf une seraient invalidées. À ce stade, si la sixième copie de l'encapsuleur était mutée, une implémentation agressive de copie à l'écriture pouvait se rendre compte qu'elle contenait la seule référence à sa chaîne, et donc décider qu'une copie était inutile. L'implémentation que je décris, cependant, créerait une nouvelle copie mutable et abandonnerait l'immuable. Malgré le fait qu'il y ait quelques opérations de copie supplémentaires, cependant, la réduction du surdébit de filetage devrait, dans la plupart des cas, plus que compenser le coût. Si la majorité des copies logiques produites ne sont jamais mutées, cette approche peut être plus efficace que de toujours faire des copies de chaînes.
Quelle est votre stratégie d'allocation de mémoire? Vous pouvez compter sur Pool Allocation pour améliorer les performances. –
J'espère que c'est juste pour apprendre. Il y a tellement d'embûches pour que cela fonctionne correctement dans toutes les situations. –
juste à des fins d'apprentissage les gars .. – fiveOthersWaiting