2010-11-10 25 views
0

Les classes:question de verrouillage de base en C#

public class SomeCollection 
{ 
    public void IteratorReset() 
    { 
     index = -1; 
    } 

    public bool IteratorNext() 
    { 
     index++; 
     return index < Count; 
    } 

    public int Count 
    { 
     get 
     { 
      return floatCollection.Count; 
     } 
    } 

    public float CurrentValue 
    { 
     get 
     { 
     return floatCollection[index]; 
     } 
    } 

    public int CurrentIndex 
    { 
     get 
     { 
     return intCollection[index]; 
     } 
    } 
} 

classe qui tient référence à 'SomeCollection':

public class ThreadUnsafeClass 
{  
    public SomeCollection CollectionObj 
    { 
     get 
     { 
      return collectionObj; 
     }    
    }  
} 

Classes ClassA, ClassB et ClassC contiennent la boucle suivante qui itère sur CollectionObj:

for (threadUnsafeClass.CollectionObj.IteratorReset(); threadUnsafeClass.CollectionObj.IteratorNext();) 
{ 
    int currentIntIndex = threadUnsafeClass.CollectionObj.CurrentIndex; 
    float currentfloatValue = threadUnsafeClass.CollectionObj.CurrentValue; 

    // ...  
} 

Puisque je suis seulement lecture CollectionObj dans les 3 classes, j'utilise multithreading pour l'accélération, mais je ne suis pas sûr de savoir comment faire appliquer la sécurité des threads. J'ai ajouté un verrou dans ThreadUnsafeClass lors de la récupération de CollectionObj, mais l'application génère une exception hors de portée.

Toute aide est appréciée.

Merci!

Répondre

5

Vous lisez uniquement la propriété CollectionObj, mais vous mouvez alors l'objet auquel la valeur fait référence. Voir ce bit:

for (threadUnsafeClass.CollectionObj.IteratorReset(); 
    threadUnsafeClass.CollectionObj.IteratorNext();) 

Les deux IteratorReset et IteratorNext muter SomeCollection en changeant la valeur de index. Fondamentalement, vous ne pouvez pas le faire en toute sécurité avec votre code actuel. Plusieurs threads peuvent tous appeler IteratorNext() en même temps, par exemple. Le premier appel renvoie true, mais avant que ce thread ne puisse lire les valeurs, les autres threads rendent l'index invalide. Pourquoi utilisez-vous la collection pour l'itération? Généralement, vous implémentez IEnumerable<T> et renvoyez un nouvel objet dans GetEnumerator. De cette façon, des threads différents pourraient chacun obtenir un objet différent représentant "leur" curseur sur la même collection. Ils pourraient tous itérater dessus, et tous voir toutes les valeurs.

+0

Merci Jon.Quel impact le retour d'un nouvel objet dans GetEnumerator() a-t-il sur les performances étant donné que ma collection est généralement très grande et que la section loop est appelée des centaines de milliers de fois? – alhazen

+1

@alhazen: Si la collection est très grande, cela * réduira * l'impact de la création d'un objet supplémentaire - parce que vous n'en créez pas un à chaque itération, juste une fois pour toute la boucle. Vous * pourriez * utiliser un type de valeur si vous * vouliez vraiment, comme 'List ', mais je ne ferais certainement pas cela tant que vous ne saviez pas que vous aviez un problème. –

+0

Désolé de vous corriger à nouveau. J'ai modifié 'SomeCollection' pour qu'il implémente' IEnumerable 'comme vous l'avez suggéré. Actuellement, GetEnumerator() renvoie 'floatListCollection.GetEnumerator()' mais je n'ai pas pu trouver de message décrivant un moyen de renvoyer un nouvel objet énumérateur à chaque appel. Puis-je utiliser le clonage dans ce cas? Merci. – alhazen

1

Le verrouillage de la propriété CollectionObj n'aidera pas. Un problème possible est que tous les 3 threads appellent IteratorReset(), ce qui définit l'index à -1. Imaginez le scénario où A commence la boucle for, et arrive à la première ligne de la boucle avant d'être interrompue. Maintenant B arrive et appelle IteratorReset(), puis est interrompu pour laisser A s'exécuter à nouveau. Thread A exécute la propriété CurrentIndex, qui utilise en interne index = -1 en raison de B en cours d'exécution. Boom, exception hors de portée.

Il existe d'autres façons de générer de mauvais résultats, mais c'est probablement le plus facile à voir. Est-ce que l'intention est que chacun des trois fils passe par chaque élément? Ou attendez-vous que A, B et C divisent le travail (comme une file d'attente de consommateurs)?

+0

Merci pour votre réponse. Tous les threads parcourent les mêmes éléments de la collection. – alhazen

+0

Si tel est le cas, quelque chose dans le sens de ce que suggère Jon Skeet serait le meilleur. Si vous avez besoin de chaque thread pour diviser le travail, vous pouvez modifier l'implémentation de SomeCollection pour renvoyer un tuple sur GetNext(), en verrouillant l'implémentation de cette méthode dans une section critique. –

1

L'objet SomeCollection est référencé par chacune des trois classes A, B et C, chacune tentant d'incrémenter l'index interne, provoquant l'erreur (les erreurs). Cela dit, vous devriez être en mesure de lire des objets dans un tableau à partir de plusieurs threads avec quelque chose comme ce qui suit:

public static object[] sharedList = new object[]{1,2,3,4,5}; 
public void Worker() 
{ 
    int localSum=0; 
    for(int i=0; i<sharedList.length; i++){ 
     localSum += (int)sharedList[i]; 
    } 
} 

L'important est que chaque fil conservera son propre emplacement dans le tableau, contrairement à la collectionObj .