2010-12-29 10 views
4

In goofing um mit einigen F # (via MonoDevelop), habe ich eine Routine geschrieben, die mit einem Thread-Dateien in einem Verzeichnis aufgelistet:helfen mir Grund zu F # Fäden

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

Und dann eine asynchrone Version davon:

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 
      [||] 
    ) 

Auf kleinen Verzeichnissen funktioniert die asynchrone Version einwandfrei. Bei größeren Verzeichnissen (z. B. vielen Tausenden von Verzeichnissen und Dateien) scheint die asynchrone Version zu hängen. Was vermisse ich?

Ich weiß, dass das Erstellen von Tausenden von Threads wird nie die effizienteste Lösung sein - ich habe nur 8 CPUs - aber ich bin verblüfft, dass für größere Verzeichnisse die asynchrone Funktion nicht reagiert (auch nach einer Hälfte Stunde). Es versagt jedoch nicht sichtbar, was mich verblüfft. Gibt es einen Threadpool, der erschöpft ist?

Wie funktionieren diese Threads tatsächlich?

Edit:

Nach this document:

Mono> = 2.8.x hat einen neuen Threadpool, die viel, viel schwerer Deadlock. Wenn Sie einen Threadpool-Deadlock erhalten, besteht die Möglichkeit, dass Ihr Programm versucht, sich zu blockieren.

: D

+0

klingt wie Deadlock ..... –

+2

WRT zu einem Deadlock, das ist sehr wahrscheinlich. Betrachten Sie den Fall, in dem das Verzeichnis von Ordner B beendet werden soll, müssen Sie X-Threads zum Thread-Pool hinzufügen. Dies ist jedoch blockiert, bis die vorherigen Threads beendet sind. außer dass sie blockiert werden, indem sie mehr Threads im Thread-Pool erzeugen müssen ... –

+1

Für verwaltete Stacktraces eines gehangenen Programms $ PID, "kill -QUIT $ PID" und überprüfe die Konsolenausgabe des Programms. Für native Stacktraces, "gdb attach $ PID" und dann "t a a bt". –

Antwort

6

Ja, die meisten wahrscheinlich, dass Sie den Mono-Thread-Pool sind überwältigend, die die Leistung Ihres Systems zum Stillstand schleift.

Wenn Sie eine Sache von diesem erinnern, ist es, dass Threads sind teuer. Jeder Thread benötigt einen eigenen Stack (Größe in Megabyte) und einen Teil der CPU-Zeit (erfordert Kontextwechsel). Aus diesem Grund ist es selten eine gute Idee, einen eigenen Thread für kurzlebige Aufgaben zu erstellen. Deshalb hat .NET einen ThreadPool.

Ein ThreadPool ist eine bestehende Sammlung von Threads für kurze Aufgaben, und es ist, was F # Benutzer für seine Async-Workflows. Wenn Sie eine F # -Assync-Operation ausführen, wird die Aktion einfach an den Thread-Pool delegiert.

Das Problem ist, was passiert, wenn Sie Tausende von asynchronen Aktionen in F # alle auf einmal spawnen? Eine naive Implementierung würde einfach so viele Threads erzeugen wie nötig. Wenn Sie jedoch 1.000 Threads benötigen, benötigen Sie 1.000 x 4 MB Stack-Speicherplatz. Selbst wenn Sie genug Speicher für alle Stapel hätten, würde Ihre CPU ständig zwischen den verschiedenen Threads wechseln. (Und paging die lokalen Stacks in und aus dem Speicher.)

IIRC, die Windows .NET-Implementierung war schlau genug, nicht eine Tonne Threads spawnen und einfach die Arbeit in die Warteschlange, bis es einige freie Threads waren, um die Aktionen durchzuführen . Mit anderen Worten, es würde Threads hinzufügen, bis es eine feste Nummer hatte und diese einfach verwenden. Ich weiß jedoch nicht, wie der Thread-Pool von Mono implementiert ist.

tl; dr: Dies funktioniert wie erwartet.

+0

Ich denke, du hast recht: hier mit Mono, fsi.exe startet eine Reihe von Threads, aber da sie meist nichts tun, ist das System überhaupt nicht gestresst ... –

0

Chris hat wahrscheinlich Recht. Der andere zu berücksichtigende Aspekt ist, dass Dateisysteme keine festen Dinge sind - werden diese Verzeichnisse mit Tausenden von Dateien geändert, während Sie versuchen, die Liste zu verarbeiten? Wenn das der Fall ist, könnte das irgendwo einen Wettlauf verursachen.

+0

Dies sind Nicht-System-Verzeichnisse mit nichts aufregendem passiert so, aber das wäre ein Anliegen für eine echte App. Ich denke, das ist ein Grund, warum die System.IO.Directory-Methoden Arrays anstelle von Listen zurückgeben: Das Dateisystem ist veränderbar und wird sowieso nicht von der Laufzeit gesteuert. –

Verwandte Themen