2010-12-29 6 views
4

En ratant son coup autour avec quelques F # (via MonoDevelop), je l'ai écrit une routine qui énumère les fichiers dans un répertoire avec un fil:me aider à raisonner sur F fils #

let rec loop (path:string) = 
    Array.append 
    (
     path |> Directory.GetFiles 
    ) 
    (
     path 
     |> Directory.GetDirectories 
     |> Array.map loop 
     |> Array.concat 
    ) 

Et puis une version asynchrone de celui-ci:

let rec loopPar (path:string) = 
    Array.append 
    ( 
     path |> Directory.GetFiles 
    ) 
    ( 
     let paths = path |> Directory.GetDirectories 
     if paths <> [||] then 
      [| for p in paths -> async { return (loopPar p) } |] 
      |> Async.Parallel 
      |> Async.RunSynchronously 
      |> Array.concat 
     else 
      [||] 
    ) 

Sur les petits répertoires, la version asynchrone fonctionne correctement. Sur les plus gros répertoires (par exemple, plusieurs milliers de répertoires et de fichiers), la version asynchrone semble se bloquer. Qu'est-ce que je rate? Je sais que la création de milliers de threads ne sera jamais la solution la plus efficace - je n'ai que 8 processeurs - mais je suis déconcerté que pour les plus gros répertoires la fonction asynchrone ne répond pas (même après une moitié heure). Cependant, cela n'échoue pas visiblement, ce qui me déroute. Y a-t-il un pool de threads qui est épuisé?

Comment ces threads fonctionnent-ils réellement?

Edit:

Selon this document:

Mono> = 2.8.x a une nouvelle threadpool qui est beaucoup, beaucoup plus difficile à une impasse. Si vous obtenez une impasse threadpool, il y a des chances que votre programme tente d'être bloqué.

: D

+0

ressemble à une impasse ..... –

+2

WRT à un blocage, c'est très probable. Considérez le cas où pour terminer le dossier du dossier B, vous devez ajouter des threads X au pool de threads. Cependant, cela est bloqué jusqu'à ce que les threads précédents soient terminés; sauf qu'ils sont bloqués en ayant besoin de générer plus de threads dans le pool de threads ... –

+1

Pour les stacktraces gérés d'un programme bloqué $ PID, "kill -QUIT $ PID" et vérifiez la sortie de la console du programme. Pour les stacktraces natifs, "gdb attach $ PID" puis "t a a bt". –

Répondre

6

Oui, très probablement vous terrassant le pool de threads Mono qui meuler les performances de votre système à l'arrêt.

Si vous vous rappelez une chose de ceci, c'est que filets sont chers. Chaque thread a besoin de sa propre pile (taille de mégaoctets) et d'une tranche de temps processeur (nécessitant un changement de contexte). Pour cette raison, il est rarement une bonne idée de créer votre propre fil pour des tâches de courte durée. C'est pourquoi .NET a un ThreadPool. Un ThreadPool est une collection existante de threads pour des tâches courtes, et c'est ce que F # utilisateurs pour ses flux de travail asynchrones. Chaque fois que vous exécutez une opération F # Asynchrone, il délègue simplement l'action au pool de threads.

Le problème est: que se passe-t-il lorsque vous lancez des milliers d'actions asynchrones en F # en une fois? Une implémentation naïve générerait simplement autant de threads que nécessaire. Cependant, si vous avez besoin de 1 000 threads, cela signifie que vous avez besoin de 1 000 x 4 Mo d'espace de pile. Même si vous avez suffisamment de mémoire pour toutes les piles, votre CPU changera constamment entre les différents threads. (Et pagination des piles locales dans et hors de la mémoire.)

IIRC, l'implémentation Windows .NET a été assez intelligente pour ne pas engendrer une tonne de threads et simplement mettre en file d'attente le travail jusqu'à ce qu'il y avait quelques threads de rechange pour effectuer les actions . En d'autres termes, il continuerait à ajouter des threads jusqu'à ce qu'il ait un nombre fixe et juste les utiliser. Cependant, je ne sais pas comment le pool de threads de Mono est implémenté.

tl; dr: Cela fonctionne comme prévu.

+0

Je pense que vous avez raison: ici avec Mono, fsi.exe démarre un tas de threads, mais comme ils ne font généralement rien, le système n'est pas du tout stressé ... –

0

Chris a probablement raison. L'autre angle à considérer est que les systèmes de fichiers ne sont pas des choses fixes - ces répertoires avec des milliers de fichiers changent-ils lorsque vous essayez de traiter la liste? Si c'est le cas, cela pourrait causer une situation de concurrence quelque part.

+0

Ce sont des répertoires non-système avec rien d'excitant qui se passe comme ça, mais ce serait une préoccupation pour une vraie application. Je suppose que c'est une des raisons pour lesquelles les méthodes System.IO.Directory renvoient des tableaux au lieu de listes: le système de fichiers est mutable et non contrôlé par le moteur d'exécution, de toute façon. –

Questions connexes