Existe-t-il une version asynchrone de DirectoryInfo?GetFiles / Directory.GetDirectories in dotNet?
Existe-t-il une version asynchrone de DirectoryInfo?GetFiles / Directory.GetDirectories in dotNet? J'aimerais les utiliser dans un bloc F# async, et ce serait bien d'avoir une version qui peut être appelée avec des Asyncallbacks.
le problème c'est que j'essaie d'aspirer un tas de répertoires, probablement sur des montages SMB sur des connexions réseau lentes, et je ne veux pas d'un tas de threads de piscine assis autour en attendant des lectures réseau alors qu'ils pourraient faire d'autres travaux.
8 réponses
Non, je ne pense pas qu'il y est. L'approche du "fil rouge" est probablement la plus pragmatique. Alternativement, je suppose que vous pourriez tomber à P / Invoke-mais ce serait un beaucoup plus de travail.
Je n'ai pas trouvé de version async de GetFiles, cependant si vous regardez le code source pour d'autres opérations Async, elles sont définies comme suit:
module FileExtensions =
let UnblockViaNewThread f =
async { //let ctxt = System.Threading.SynchronizationContext.Current
do! Async.SwitchToNewThread ()
let res = f()
do! Async.SwitchToThreadPool ()
//do! Async.SwitchTo ctxt
return res }
type System.IO.File with
static member AsyncOpenText(path) = UnblockViaNewThread (fun () -> System.IO.File.OpenText(path))
static member AsyncAppendText(path) = UnblockViaNewThread (fun () -> System.IO.File.AppendText(path))
static member AsyncOpenRead(path) = UnblockViaNewThread (fun () -> System.IO.File.OpenRead(path))
static member AsyncOpenWrite(path) = UnblockViaNewThread (fun () -> System.IO.File.OpenWrite(path))
static member AsyncOpen(path,mode,?access,?share) =
let access = match access with Some v -> v | None -> System.IO.FileAccess.ReadWrite
let share = match share with Some v -> v | None -> System.IO.FileShare.None
UnblockViaNewThread (fun () -> System.IO.File.Open(path,mode,access,share))
static member OpenTextAsync(path) = System.IO.File.AsyncOpenText(path)
static member AppendTextAsync(path) = System.IO.File.AsyncAppendText(path)
static member OpenReadAsync(path) = System.IO.File.AsyncOpenRead(path)
static member OpenWriteAsync(path) = System.IO.File.AsyncOpenWrite(path)
static member OpenAsync(path,mode,?access,?share) = System.IO.File.AsyncOpen(path, mode, ?access=access, ?share=share)
en d'autres termes, les opérations Async, streamreader et WebClient ne sont que des enveloppements autour des opérations syncrones, donc vous devriez être capable d'écrire votre propre enveloppement autour de GetFiles/GetDirectories comme suit:
module IOExtensions =
type System.IO.Directory with
static member AsyncGetFiles(directory) = async { return System.IO.Directory.GetFiles(directory) }
static member AsyncGetDirectories(path) = async { return System.IO.Directory.GetDirectories(path) }
Cela peut être considéré comme un peu un hack, mais vous pouvez envisager d'utiliser le UWP StorageFolder API.
exemple en C# (si F# est probablement tout aussi simple):
using Windows.Storage;
...
var folder = await StorageFolder.GetFolderFromPathAsync(path);
var files = await folder.GetFilesAsync();
var folders = await folder.GetFoldersAsync();
vous pouvez facilement les consommer à partir des applications traditionnelles .net desktop et console en utilisant le UWP pour les pc de Bureau bibliothèque de Lucian Wischik (de Microsoft).
Install-Package UwpDesktop
j'ai utilisé plusieurs fois cette approche pour obtenir des objets asynchrones à partir de fonctions / procédures, et ça a toujours bien fonctionné:
let AsyncGetDirectories path =
let fn = new Func<_, _>(System.IO.Directory.GetDirectories)
Async.BuildPrimitive(path, fn.BeginInvoke, fn.EndInvoke)
en fait, selon le aide Directory.GetFiles
,Directory.EnumerateFiles
retourner le premier résultat immédiatement (c'est un IEnumerable
), plutôt que d'attendre toute la liste avant de revenir. Je crois que c'est probablement ce que vous cherchez.
voir aussi
http://weblogs.asp.net/podwysocki/archive/2009/03/18/functional-net-laziness-becomes-you.aspx
je ne suis pas F# programmeur, mais je préfère le faire en C#:
static IEnumerable<string> IterateFiles(string path, string pattern) {
var entryQueue = new Queue<string>();
entryQueue.Enqueue(path);
while (entryQueue.Count > 0) {
var subdirs = Directory.GetDirectories(entryQueue.Peek());
var files = Directory.GetFiles(entryQueue.Peek(), pattern, SearchOption.TopDirectoryOnly);
foreach (var file in files)
yield return file;
entryQueue.Dequeue();
foreach(var subdir in subdirs)
entryQueue.Enqueue(subdir);
}
}
je suppose qu'il y a une construction similaire aux itérateurs dans F#.
la réponse de Princess est la façon d'ajouter la granularité entre les tâches - donc ce genre de chose permettrait aux autres joueurs d'utiliser le pool de thread:
let! x = OpenTextAsync("whatever");
// opening for something else to run
let! x = OpenTextAsync("whatever");
// opening for something else to run
let! x = OpenTextAsync("whatever");
cela n'aide pas autant quand chacun de ces appels de blocage est lourd - et un GetFiles sur SMB est à peu près la définition de lourd.
j'espérais qu'il y avait une sorte d'équivalent de BeginRead
/EndRead
pour les répertoires, et que GetFiles
/GetDirectories
était juste un wrapper autour de nice des appels de niveau inférieur qui ont exposé des variantes d'async. Quelque chose comme BeginReadDir
/EndReadDir
.