Есть ли асинхронная версия DirectoryInfo.GetFiles / Directory.GetDirectories в dotNet? - PullRequest
35 голосов
/ 05 апреля 2009

Существует ли асинхронная версия DirectoryInfo.GetFiles / Directory.GetDirectories в dotNet? Я хотел бы использовать их в асинхронном блоке F #, и было бы неплохо иметь версию, которую можно вызывать с помощью AsyncCallbacks.

Проблема в том, что я пытаюсь засосать кучу каталогов, вероятно, при монтировании SMB по медленным сетевым соединениям, и я не хочу, чтобы куча потоков пула сидела без дела, ожидая сетевых чтений, когда они могли бы выполнять другие работа.

Ответы [ 8 ]

13 голосов
/ 05 апреля 2009

Я не нашел асинхронную версию GetFiles, однако, если вы посмотрите на исходный код для других асинхронных операций, они определены следующим образом:

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)

Другими словами, операции Async file, streamreader и WebClient являются просто оболочками для синхронных операций, поэтому вы должны иметь возможность написать свою собственную оболочку для GetFiles / GetDirectories следующим образом:

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) }
11 голосов
/ 05 апреля 2009

Нет, я не думаю, что есть. Подход с использованием пула потоков, пожалуй, самый прагматичный. В качестве альтернативы, я думаю, вы можете перейти к P / Invoke, но это будет лот больше работы.

6 голосов
/ 21 июня 2016

Это может считаться хаком, но вы можете рассмотреть возможность использования UWP StorageFolder API .

Пример C # (хотя F #, вероятно, так же просто):

using Windows.Storage;

...

var folder = await StorageFolder.GetFolderFromPathAsync(path);
var files = await folder.GetFilesAsync();
var folders = await folder.GetFoldersAsync();

Вы можете легко использовать их из традиционных настольных и консольных приложений .NET, используя библиотеку UWP for Desktop от Lucian Wischik (из Microsoft).

Install-Package UwpDesktop
4 голосов
/ 26 января 2014

На самом деле, согласно справке для Directory.GetFiles, Directory.EnumerateFiles немедленно вернет первый результат (это IEnumerable), а не будет ждать весь список перед возвратом. Я думаю, что это, вероятно, то, что вы ищете.

3 голосов
/ 05 апреля 2009

Я несколько раз использовал этот подход для получения объектов Async из функций / процедур, и он всегда работал отлично:


let AsyncGetDirectories path = 
    let fn = new Func<_, _>(System.IO.Directory.GetDirectories)
    Async.BuildPrimitive(path, fn.BeginInvoke, fn.EndInvoke)
1 голос
/ 05 апреля 2009
0 голосов
/ 02 января 2013

Я не программист F #, но я бы делал это на 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);
    }
}

Я предполагаю, что итераторы в F # похожи по конструкции.

0 голосов
/ 05 апреля 2009

Ответ принцессы - это способ добавить гранулярность между задачами - так что подобные вещи позволят другим игрокам использовать пул потоков:

let! x = OpenTextAsync("whatever");
// opening for something else to run
let! x = OpenTextAsync("whatever");
// opening for something else to run
let! x = OpenTextAsync("whatever");

Это мало помогает, когда каждый из этих блокирующих вызовов тяжелый - а GetFiles over SMB в значительной степени является определением heavy .

Я надеялся, что есть какой-то эквивалент для BeginRead / EndRead для каталогов, и что GetFiles / GetDirectories была просто хорошей оберткой для вызовов более низкого уровня, которые выставляли некоторые асинхронные варианты. Что-то вроде BeginReadDir / EndReadDir.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...