2009-08-10 8 views
2

Je travaille sur une bibliothèque de classes et j'ai opté pour un routage avec ma conception pour faciliter légèrement la mise en œuvre et la sécurité des threads, mais je me demande s'il n'y aurait pas une meilleure approche. Un bref contexte est que j'ai un algorithme heuristique multithread dans une bibliothèque de classes, qu'une fois mis en place avec un scénario devrait tenter de le résoudre. Cependant, je veux évidemment que ce soit thread safe et si quelqu'un apporte un changement à quoi que ce soit alors qu'il est en train de résoudre pour que cela provoque des plantages ou des erreurs. L'approche actuelle que j'ai est de savoir si j'ai une classe A, alors je crée un certain nombre d'instances InternalA pour chaque instance d'A. L'InternalA possède de nombreuses propriétés importantes de la classe A, mais est interne et inaccessible à l'extérieur de la bibliothèque. L'inconvénient de ceci, est que si je souhaite étendre la logique de prise de décision (ou laisser quelqu'un faire cela en dehors de la bibliothèque), cela signifie que je dois changer le code dans l'InternalA (ou fournir une sorte de délégué fonction).Conception de bibliothèque de classes Safe Thread

Est-ce que cela vous semble être la bonne approche?

Répondre

2

Il est difficile de dire exactement de cela - mais je peut dire que si vous pouvez tout rendre immuable, votre vie sera beaucoup plus facile. Regardez comment les langages fonctionnels abordent les structures de données immuables et les collections. Les données mutables moins partagées que vous avez, le thread simple sera.

+0

Malheureusement, je ne pense pas que je peux faire des choses immuables. J'ai ajouté plus d'informations dans une autre réponse. – Ian

2

Pourquoi pas? Créer une classe générique, qui accepte les 2 membres de classe (par exemple de verrouillage/déverrouillage.) - vous pouvez donc fournir

  • impl ThreadSafe (implmenetation peut utiliser Monitor.Enter/Sortie à l'intérieur)
  • de impl de l'ensemble du système de sécurité (en utilisant Mutex)
  • N'est pas sûr, mais rapide (en utilisant un impl vide).
+0

Ce serait plutôt cool. Savez-vous s'il existe un exemple de code illustrant ce genre de choses? – Sharun

+0

c'est très simple - chaque implémentation ne dépassera pas 10 lignes de code. La classe générique accepte "Protector" - elle peut être dérivée de certains Interface IProtector avec la méthode 2: Enter() Exit() Outil pour vos valises. Et dans votre code code surround surround avec paire: essayez { MyProtector.Enter(); ...... } finally {MyProtector.Exit(); } Ou faire la même chose avec IDisposable Si vous avez empilé avec ceci écrivez-moi à mvoronoy (*) yahoo (.) Com – Dewfy

+0

Dewfy, merci pour la réponse. Je pourrais vous envoyer un courriel à un moment donné pour obtenir un exemple plus approfondi, car je ne suis pas tout à fait sûr que je suis 100%. – Ian

1

Une autre façon avec laquelle j'ai eu du succès est d'utiliser des interfaces pour réaliser une séparation fonctionnelle. le coût de cette approche est que vous vous retrouvez avec des champs «répétés» car chaque interface nécessite une séparation totale des autres champs.

Dans mon cas, j'avais 2 threads qui ont besoin de passer sur un ensemble de données qui est potentiellement grand et nécessite le moins de garbage collection possible. C'est-à-dire que je veux seulement transmettre des informations de changement de la première étape à la seconde. Et puis avoir le premier processus la prochaine unité de travail.

ceci a été réalisé en utilisant des tampons de changement pour passer les changements d'une interface à l'autre.

cela permet à un thread de travailler sur une interface, de faire toutes ses modifications et de publier ensuite une structure contenant les changements que l'autre interface (thread) doit appliquer avant son travail. Pour ce faire, vous disposez d'un double tampon ... (le thread 1 produit un rapport de changement tandis que le thread 2 consomme le dernier rapport). Si vous ajoutez plus d'interfaces (et de threads), il apparaît qu'il y a des pulsations de travail se déplaçant dans les threads.

Cela a été basé sur mes recherches et je n'ai aucun doute qu'il existe de meilleures méthodes disponibles maintenant. Mon but en arrivant avec ceci cependant était d'éviter le besoin de serrures dans la grande majorité du code en concevant des conditions de course.l'autre considération majeure est la performance dans la collecte des ordures - qui peut ne pas être un problème pour vous. De cette façon tout va bien jusqu'à ce que vous ayez besoin d'interactions complexes entre les threads ... alors vous commencez à forcer la disposition de vos structures de tampons pour les réutiliser pour contourner l'héritage qui à son tour a un coût supplémentaire.

0

Un peu plus d'informations sur le problème pour vous aider ...

L'heuristique J'utilise est de résoudre des problèmes comme TSP. Ce qui se passe dès le début de chaque calcul est que tous les aspects qui forment le problème (vendeur/lieux à visiter) sont clonés afin qu'ils ne soient pas affectés à travers les threads. Cela signifie que chaque thread peut changer les données (telles que le stock laissé sur un vendeur, etc.) car il y a un nombre de valeurs qui changent pendant le calcul au fur et à mesure que les choses progressent. Ce que je voudrais vraiment faire est de permettre vérifié comme HasSufficientStock() pour un exemple simple à remplacer par un développeur utilisant la bibliothèque. Toutefois, actuellement, pour ajouter une protection supplémentaire à travers les threads et les makings, des classes plus simples et plus légères, je les convertis en classes internes, et ce sont elles qui sont réellement utilisées et clonées.

Par exemple

class A 
{ 
    public double Stock { get; } 

    // Processing and cloning actually works using these InternalA's 
    internal InternalA ConvertToInternal() {} 
} 

internal class InternalA : ICloneable 
{ 
    public double Stock { get; set; } 

    public bool HasSufficientStock() {} 
} 
Questions connexes