2010-07-10 7 views
0

J'ai une liste et deux threads qui utilisent la liste.Deux threads utilisant la même variable, produit un problème

Le premier thread obtient de nouvelles connexions, chaque nouvelle connexion est ajoutée à la liste.

Le deuxième thread en boucle sur la liste pour gérer les connexions (en utilisant foreach).

Le problème est que, parfois, lorsque le second thread est en boucle sur la liste, la liste change avant la fin de la boucle. J'ai même essayé de créer une nouvelle copie de la liste et de la boucler. Mais cela produit d'autres problèmes.

Je ne veux pas créer un nouveau thread pour gérer chaque nouvelle connexion, car j'ai compris que trop de threads peuvent nuire aux performances. Y a-t-il un autre moyen de gérer les connexions?

+3

Si vous apprenez à faire du filetage, ne croyez pas les autres quand ils disent que «trop de threads peuvent nuire aux performances». Faites-le vous-même et découvrez quelles sont les limites de votre situation. Vous apprendrez beaucoup plus de cette façon. –

+0

Quels types de problèmes surviennent lors de la copie de la liste? – SwDevMan81

+0

Il indique que le tableau cible n'est pas dans la bonne taille (le tableau change avant la fin de la copie). –

Répondre

3

2 problèmes.

1) Vous avez besoin de verrouiller la liste. Vous devez vous assurer que vous avez un accès exclusif mutuel à la liste, de sorte qu'il ne peut pas être énuméré pendant qu'il est modifié. Une première solution consiste à utiliser des verrous:

class Mailbox { 
    List<int> list; 

    void Send(int a) { 
     lock(list) { 
       list.Add(a); 
     } 
    } 

    int Receive() { 
     lock(list) { 
      // Enumerate 
      return ...; 
     } 
     } 
} 

Plus élégante, vous pouvez utiliser l'une des nouvelles collections dans l'espace de noms Concurrent, comme BlockingCollection. Ce dernier n'est pas sûr pour l'énumération, mais fournit une méthode Take() qui peut être utilisée pour extraire des objets de celui-ci, pendant que les producteurs les insèrent.

2) Évitez de créer un fil de gazillion. Vous pouvez utiliser le .NET thread pool pour mettre en file d'attente autant de requêtes que vous le souhaitez, et le framework s'occupera de les mapper sur des threads réels, sans tuer le système.

+0

'BlockingCollection' ne synchronise que * modifications * à la liste. L'itération nécessite toujours un verrou explicite. –

+0

Oui, vous avez raison, l'énumération doit être éliminée et on peut utiliser 'Take()' à la place. – Mau

-4

Je ne sais vraiment pas grand-chose à ce sujet, mais la première idée qui me vint à l'esprit était:

magasin la longueur de la liste et faire la boucle aller

while (var i < lengthoflist) 
{ 
//whatever fancy stuff your code does 
i++; 
} 

Mais je sais Rien sur les sockets, c'était juste une idée générique que j'ai imaginée, donc je ne sais pas si ça marchera.

+0

Cela ne marchera pas (en toute sécurité). Il ne jettera probablement pas, mais il pourrait sauter des objets ou des trucs comme ça. –

1

La solution la plus simple consiste à utiliser un lock dans la liste de chaque thread.

fil Première:

lock(yourList) 
{ 
    yourList.Add(...); 
} 

fil Deuxième:

lock(yourList) 
{ 
    foreach(var item in yourList) 
    { 
     ... 
    } 
} 

Cela permettra d'éviter le premier fil d'être d'ajouter une connexion tandis que le second fil est au milieu de itérer dessus. Si le second thread itère sur la liste et que le premier essaye d'entrer dans la section lock -ed du code, il attendra que le second thread finisse de boucler la liste.

+0

@Jouke: Oui, ce que j'ai déclaré à la fin de la réponse. C'est ce que le PO demandait. –

1

Comme d'autres l'ont écrit, vous devrez utiliser le verrouillage pour gérer la concurrence avec ces deux threads.

En ce qui concerne l'évitement de plusieurs threads, je pense que cela dépend en grande partie du nombre de threads dont nous parlons. Si vous n'avez que quelques connexions de socket avec lesquelles vous travaillez, alors peut-être que faire des lectures synchrones à partir de chacun dans son propre thread est correct. Mais si vous parlez de nombreuses connexions de socket, je n'utiliserais pas de thread dédié pour chacune d'entre elles.

De loin le moyen le plus efficace de lire depuis plusieurs sockets est d'utiliser une lecture asynchrone (Socket.BeginReceive()). Sous le capot, ces méthodes async utilisent I/O Completion Ports, qui sont très efficaces. Il est relativement facile d'implémenter des serveurs TCP personnalisés capables de gérer plusieurs milliers de connexions simultanées à l'aide des méthodes asynchrones Socket.

Questions connexes