Ich spiele im Moment mit dem MailboxProcessor
. Deshalb habe ich mir ein paar Agenten ausgedacht, die ein Verzeichnis auf dem Computer und alle Unterverzeichnisse durchsuchen können - und dann die Dateien in jedem Verzeichnis ausgeben:
let fileCollector =
MailboxProcessor.Start(fun self ->
let rec loop() =
async { let! file = self.Receive()
printfn "%s" file
return! loop() }
loop())
let folderCollector =
MailboxProcessor.Start(fun self ->
let rec loop() =
async { let! dir = self.Receive()
do! Async.StartChild(
async { let! files = Directory.AsyncGetFiles dir
for z in files do fileCollector.Post z }) |> Async.Ignore
return! loop() }
loop())
let crawler =
MailboxProcessor.Start(fun self ->
let rec loop() =
async { let! dir = self.Receive()
folderCollector.Post dir
do! Async.StartChild(
async { let! dirs = Directory.AsyncGetDirectories dir
for z in dirs do self.Post z }) |> Async.Ignore
return! loop() }
loop())
crawler.Post @"C:\Projects"
printfn "Done" // Message getting fired right away, due to the async stuff.
Woran würde ich nun erkennen, dass die folderCollector
, fileCollector
y crawler
durchgeführt werden, so dass die printfn
Anweisung am Ende, würde aufgerufen werden, NACHDEM der Crawler erfolgreich alle Unterverzeichnisse durchforstet und alle Dateien ausgedruckt hat?
Aktualisierung: Durch die Anwendung der von Tomas Petricek in http://tomasp.net/blog/parallel-extra-image-pipeline.aspx habe ich mir folgenden Code ausgedacht:
let folders = new BlockingQueueAgent<string>(100)
let files = new BlockingQueueAgent<string>(100)
let rec folderCollector path =
async { do! folders.AsyncAdd(path)
do! Async.StartChild(
async { let! dirs = Directory.AsyncGetDirectories path
for z in dirs do
do! folderCollector z }) |> Async.Ignore }
let fileCollector =
async { while true do
let! dir = folders.AsyncGet()
do! Async.StartChild(
async { let! fs = Directory.AsyncGetFiles dir
for z in fs do
do! files.AsyncAdd z }) |> Async.Ignore }
let rec printFiles() =
async { let! file = files.AsyncTryGet(75)
match file with
| Some s ->
printfn "%s" s
return! displayFiles()
| None -> () }
let cts = new CancellationTokenSource()
Async.Start(folderCollector @"C:\Projects", cts.Token)
Async.Start(fileCollector, cts.Token)
Async.RunSynchronously(printFiles(), cancellationToken = cts.Token)
printfn "DONE!"
Aktualisierung: Update: In Ordnung, ich habe folgenden Code durcheinander gebracht:
let folders = new BlockingQueueAgent<string option>(10)
let files = new BlockingQueueAgent<string option>(10)
let folderCollector path =
async { let rec loop path =
async { do! folders.AsyncAdd(Some path)
let! dirs = Directory.AsyncGetDirectories path
do! [ for z in dirs -> loop z ] |> Async.Parallel |> Async.Ignore }
do! loop path
do! folders.AsyncAdd(None) }
let rec fileCollector() =
async { let! dir = folders.AsyncGet 125
match dir with
| Some s ->
let fs = Directory.GetFiles s
do! [ for z in fs -> printfn "%s" z; files.AsyncAdd(Some z) ] |> Async.Parallel |> Async.Ignore // <-- Fails silence if files are full
do! fileCollector() // <-- unreachable
| None -> printfn "Done!"; ()}
Das sieht doch gut aus, oder? Aus irgendeinem Grund ist der do! fileCollector()
Zeile in der fileCollector()
Funktion, wird nicht ausgeführt wenn die files
BlockingQueueAgent ist voll. Stattdessen scheitert es an der Stille.
Aber wenn ich es tue:
let folderCollector path =
async { let rec loop path =
async { do! folders.AsyncAdd(Some path)
let! dirs = Directory.AsyncGetDirectories path
do! [ for z in dirs -> loop z ] |> Async.Parallel |> Async.Ignore }
do! loop path
do! folders.AsyncAdd(None) }
let rec fileCollector() =
async { let! dir = folders.AsyncGet 75
match dir with
| Some s ->
let fs = Directory.GetFiles s
do! Async.StartChild(async { do! [ for z in fs -> printfn "%s" z; files.AsyncAdd(Some z) ]
|> Async.Parallel |> Async.Ignore } ) |> Async.Ignore
do! fileCollector()
| None -> printfn "Done!"; ()}
Es funktioniert einwandfrei. Allerdings kann ich jetzt nicht mehr verfolgen, wann die fileCollector
fertig ist, da es eine Reihe von asynchronen Berechnungen durchführt und daher selbst bei "None" in der Warteschlange noch etwas zu tun haben könnte. Was ist hier los?
Aktualisierung: Ich habe die fileCollector
zum gleichen "Stil" wie folderCollector
aber das Problem bleibt bestehen. Die geänderte Version:
let fileCollector() =
async { let rec loop() =
async { let! dir = folders.AsyncGet 750
match dir with
| Some s ->
let! fs = Directory.AsyncGetFiles s
do! [ for z in fs -> printfn "%A" z; files.AsyncAdd(Some z) ]
|> Async.Parallel |> Async.Ignore
return! loop()
| None -> printfn "Done!"; () }
do! loop()
printfn "after" // Never gets this far...
do! files.AsyncAdd(None) }