Существует ли асинхронная версия DirectoryInfo.GetFiles / Directory.GetDirectories в dotNet?

StackOverflow https://stackoverflow.com/questions/719020

Вопрос

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

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

Это было полезно?

Решение

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

Другие советы

Я не нашел асинхронную версию 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) }

Это может считаться небольшим взломом, но вы могли бы рассмотреть возможность использования 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 для рабочего стола библиотека Люциана Вишика (из Microsoft).

Install-Package UwpDesktop

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

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


let AsyncGetDirectories path = 
    let fn = new Func<_, _>(System.IO.Directory.GetDirectories)
    Async.BuildPrimitive(path, fn.BeginInvoke, fn.EndInvoke)

Я не программист на 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 # существует аналогичная конструкция для итераторов.

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

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 через SMB в значительной степени соответствует определению тяжелый.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top