2010-12-01 4 views
3

Dans un fil "A", je veux lire un fichier très long, et comme cela arrive, je veux envoyer chaque nouvelle ligne lue à un autre fil "B", qui ferait quelque chose pour eux. Fondamentalement, je ne veux pas attendre que le chargement du fichier se termine avant de commencer à traiter les lignes. (Je veux vraiment 2 threads et la communication entre eux, je n'ai jamais fait cela auparavant et je veux apprendre)C# - Flux de texte en direct d'un fil à l'autre

Alors, comment vais-je faire cela? Le thread A doit attendre que le thread B finisse de traiter la "ligne en cours", avant que le thread A n'envoie une autre ligne au thread B. Mais cela ne sera pas efficace; pourquoi pas un tampon dans le thread B? (pour attraper les lignes)

Veuillez également donner un exemple des méthodes que j'ai à utiliser pour cette communication croisée puisque je n'ai trouvé/vu aucun exemple utile.

Merci.

Répondre

4

Tout d'abord, il n'est pas clair que deux threads seront nécessairement utiles ici. Un seul thread lisant une ligne à la fois (ce qui est assez facile avec StreamReader) et le traitement de chaque ligne que vous allez peut-être effectuer au moins aussi bien. Les lectures de fichiers sont mises en mémoire tampon et le système d'exploitation peut lire le code avant de demander des données. Dans ce cas, la plupart de vos lectures seront terminées immédiatement car la ligne suivante a déjà été lue à l'avance par le système d'exploitation. dois attendre parce que les données ne sont pas là sur le disque. (Et le fait d'avoir 2 threads en attente du disque ne fait pas avancer les choses plus vite que d'avoir 1 thread en attente.) Le seul avantage possible est d'éviter le temps mort en lisant la prochaine lecture avant de terminer le précédent. mais l'OS fera souvent cela pour vous dans tous les cas. Ainsi, les avantages du multithreading seront au mieux marginaux ici.

Cependant, puisque vous dites que vous faites cela comme un exercice d'apprentissage, qui ne peut pas être un problème ...

j'utiliser un BlockingCollection<string> comme mécanisme pour transmettre les données d'un fil à l'autre. (Tant que vous utilisez .NET 4 ou plus tard, et sinon ... je vous suggère de passer à .NET 4 - cela simplifiera considérablement cette tâche.) Vous allez lire une ligne du fichier et la mettre dans la collection d'un fil:

string nextLine = myFileReader.ReadLine(); 
myBlockingCollection.Add(nextLine); 

Et puis un autre thread peut récupérer les lignes de ce qui suit:

while (true) 
{ 
    string lineToProcess = myBlockingCollection.Take(); 
    ProcessLine(lineToProcess); 
} 

qui va laisser le fil de la lecture dans le fichier aussi rapidement que le disque permettra aux lui, tandis que le thread de traitement traite les données à tout rythme qu'il peut. La méthode Take s'assied simplement et attend si votre thread de traitement devance le thread de lecture de fichier. Un problème avec ceci est que votre thread de lecture pourrait aller de l'avant si le fichier est volumineux et votre traitement est lent - votre programme pourrait essayer de lire des gigaoctets de données depuis un fichier tout en n'ayant traité que les premiers kilo-octets. Il n'y a pas grand chose à faire avant de lire les données - vous voulez seulement lire un peu à l'avance. Vous pouvez utiliser la propriété de BlockingCollection<T> pour limiter les choses - si vous définissez cela à un certain nombre, alors l'appel à Add bloquera si la collection a déjà ce nombre de lignes, et votre fil de lecture ne se poursuivra pas jusqu'à ce que le La boucle de traitement traite sa ligne suivante.

Il serait intéressant de comparer les performances d'un programme en utilisant votre technique à deux threads avec celle qui lit simplement les lignes d'un fichier et les traite en boucle sur un seul thread. Vous seriez en mesure de voir ce que, le cas échéant, vous bénéficiez d'une approche multithread ici. Incidemment, si votre traitement requiert beaucoup de ressources processeur, vous pouvez utiliser une variation sur ce thème pour avoir plusieurs threads de traitement (et toujours un seul thread de lecture de fichier), car BlockingCollection<T> est parfaitement heureux d'avoir de nombreux consommateurs lisant de la collection. Bien sûr, si l'ordre dans lequel vous finissez de traiter les lignes du fichier est important, cela ne sera pas une option, car bien que vous commenciez le traitement dans le bon ordre, si vous avez plusieurs threads de traitement, il est possible qu'un thread pourrait en dépasser un autre, entraînant un achèvement hors de l'ordre.

+0

Merci Ian. Je n'ai jamais pensé que les lectures de fichiers étaient tamponnées. (même si je suis bien au courant de ce qu'on appelle BUFFEREDReader en java). Mais en effet, je voulais faire cela comme un exercice. En outre, il est utile d'avoir un thread de lecture, car il va également peupler un textBox à la volée, en plus d'envoyer des lignes au thread de traitement. Ainsi, le textBox sera complet avant la fin du traitement. J'utilise .NET 4 et je vais essayer ce que vous avez suggéré. Le traitement ne devrait pas prendre assez de temps pour exiger 'BoundedCapacity'. Merci encore. – Spectraljump

+0

Très bonne réponse. Je ne sais pas si la séquence est requise. Si oui, une de mes préoccupations est que BlockingCollection pourrait ne pas servir à cette fin. – tonyjy

+0

Si vous avez exactement un producteur et un consommateur, BlockingCollection conservera la séquence car, par défaut, il utilise une ConcurrentQueue comme implémentation sous-jacente. (Vous pouvez brancher une implémentation différente si vous le souhaitez.) Bien que la réponse à http://stackoverflow.com/questions/3825275/does-blockingcollectiont-guarantee-removal-order indique, vous pouvez forcer cela en passant explicitement une file d'attente plutôt que de s'appuyer sur le comportement par défaut. –

Questions connexes