La propriété partagée a rarement un sens
Cette réponse peut être légèrement décalée, mais je dois demander, combien de cas est-il sensé du point de vue de l'utilisateur de partager la propriété ? Au moins dans les domaines dans lesquels j'ai travaillé, il n'y en avait pratiquement aucun car sinon cela impliquerait que l'utilisateur n'a pas besoin de simplement supprimer quelque chose une fois d'un endroit, mais de le supprimer explicitement de tous les propriétaires concernés avant que la ressource ne soit réellement supprimé du système.
C'est souvent une idée d'ingénierie de niveau inférieur pour empêcher la destruction des ressources pendant que quelque chose d'autre y accède, comme un autre thread. Souvent, lorsqu'un utilisateur demande à fermer / supprimer / supprimer quelque chose du logiciel, il doit être supprimé dès que possible (chaque fois qu'il est sûr de le supprimer), et il ne doit certainement pas s'attarder et provoquer une fuite de ressources aussi longtemps que l'application est en cours d'exécution.
Par exemple, un élément de jeu dans un jeu vidéo peut référencer un matériau de la bibliothèque de matériaux. Nous ne voulons certainement pas, disons, un crash de pointeur pendant si le matériau est supprimé de la bibliothèque de matériaux dans un thread alors qu'un autre thread accède toujours au matériel référencé par l'actif du jeu. Mais cela ne signifie pas qu'il soit logique que les actifs du jeu partagent la propriété des matériaux qu'ils référencent avec la bibliothèque de matériaux. Nous ne voulons pas forcer l'utilisateur à supprimer explicitement le matériau de la bibliothèque de ressources et de matériaux. Nous voulons juste nous assurer que les matériaux ne sont pas supprimés de la bibliothèque de matériel, le seul propriétaire sensé des matériaux, jusqu'à ce que les autres threads aient fini d'accéder au matériel.
Fuites de ressources
Pourtant, j'ai travaillé avec une ancienne équipe qui a adopté GC pour tous les composants du logiciel. Et même si cela a vraiment aidé à nous assurer que nous n'avions jamais de ressources détruites pendant que d'autres threads y accédaient, nous avons finalement obtenu notre part de fuites de ressources .
Et il ne s'agissait pas de fuites de ressources insignifiantes qui ne dérangent que les développeurs, comme un kilo-octet de mémoire qui a fui après une session d'une heure. Il s'agissait de fuites épiques, souvent des gigaoctets de mémoire sur une session active, conduisant à des rapports de bogues. Parce que maintenant, lorsque la propriété d'une ressource est référencée (et donc partagée en propriété) entre, disons, 8 parties différentes du système, il suffit d'une seule pour ne pas supprimer la ressource en réponse à l'utilisateur qui demande qu'elle soit supprimée pour elle à fuir et éventuellement indéfiniment.
Je n'ai donc jamais été un grand fan du GC ou du comptage de références appliqué à grande échelle en raison de la facilité avec laquelle ils ont créé un logiciel qui fuit. Ce qui aurait été autrefois un crash de pointeur pendant qui est facile à détecter se transforme en une fuite de ressources très difficile à détecter qui peut facilement voler sous le radar des tests.
Des références faibles peuvent atténuer ce problème si la langue / bibliothèque les fournit, mais j'ai trouvé difficile d'obtenir une équipe de développeurs de compétences mixtes pour pouvoir utiliser systématiquement des références faibles chaque fois que cela était approprié. Et cette difficulté n'était pas uniquement liée à l'équipe interne, mais à chaque développeur de plugin unique pour notre logiciel. Eux aussi pourraient facilement provoquer une fuite de ressources du système en stockant simplement une référence persistante à un objet de manière à ce qu'il soit difficile de remonter au plugin en tant que coupable, nous avons donc également obtenu notre part du lion des rapports de bogues résultant de nos ressources logicielles être divulgué simplement parce qu'un plugin dont le code source était hors de notre contrôle n'a pas réussi à libérer des références à ces ressources coûteuses.
Solution: suppression différée et périodique
Donc, ma solution plus tard, que j'ai appliquée à mes projets personnels qui m'ont donné le meilleur que j'ai trouvé dans les deux mondes, a été d'éliminer le concept, referencing=ownership
mais qui a encore retardé la destruction des ressources.
Par conséquent, chaque fois que l'utilisateur fait quelque chose qui nécessite la suppression d'une ressource, l'API est exprimée en termes de suppression de la ressource:
ecs->remove(component);
... qui modélise la logique utilisateur de manière très simple. Cependant, la ressource (composant) ne peut pas être supprimée immédiatement s'il existe d'autres threads système dans leur phase de traitement où ils pourraient accéder simultanément au même composant.
Donc, ces threads de traitement donnent ensuite du temps ici et là, ce qui permet à un thread qui ressemble à un garbage collector de se réveiller et de " stopper le monde " et de détruire toutes les ressources dont la suppression a été demandée tout en empêchant les threads de traiter ces composants jusqu'à ce qu'il soit terminé . J'ai réglé cela pour que la quantité de travail à faire ici soit généralement minime et ne coupe pas sensiblement les fréquences d'images.
Maintenant, je ne peux pas dire que c'est une méthode éprouvée et bien documentée, mais c'est quelque chose que j'utilise depuis quelques années sans aucun mal de tête et sans fuite de ressources. Je recommande d'explorer des approches comme celle-ci lorsqu'il est possible pour votre architecture de s'adapter à ce type de modèle de concurrence, car il est beaucoup moins lourd que GC ou le comptage de références et ne risque pas ces types de fuites de ressources volant sous le radar des tests.
Le seul endroit où j'ai trouvé le comptage de références ou GC utile est pour les structures de données persistantes. Dans ce cas, c'est le territoire de la structure de données, loin des préoccupations des utilisateurs, et là, il est en fait logique que chaque copie immuable partage potentiellement la propriété des mêmes données non modifiées.