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

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

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


person James Moore    schedule 05.04.2009    source источник


Ответы (7)


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

person Marc Gravell    schedule 05.04.2009
comment
Так ли это и сейчас, когда .NET 4.5 вышел с кучей новых асинхронных API? Я искал это и не видел. - person Drew Noakes; 24.09.2012
comment
Это все еще актуально для .NET Core 2.2. - person Brugner; 10.09.2019
comment
В качестве альтернативы, я думаю, вы можете перейти к P / Invoke - я не вижу никаких Win32 Overlapped IO или собственных асинхронных API Win32 для операций файловой системы, поэтому я не уверен, как P / Invoke поможет здесь. Насколько мне известно, API асинхронной файловой системы в UWP также использует внутренние потоки вместо того, чтобы фактически перекрывать ввод-вывод или управлять прерываниями ввода-вывода. - person Dai; 06.03.2020

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

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) }
person Juliet    schedule 05.04.2009
comment
Конечно, это не настоящий асинхронный ввод-вывод, но, тем не менее, это хорошее практическое решение. Если вам нужны действительно асинхронные операции, вам придется использовать какое-то ужасное взаимодействие Win32, которое, как я полагаю, не стоит того в контексте ... - person Noldorin; 05.04.2009
comment
Кроме того, в наши дни я использую F # на iOS через Xamarin (а ​​вскоре и на Android), поэтому какое-то ужасное взаимодействие с Win32 даже невозможно. - person James Moore; 12.12.2013

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

Install-Package UwpDesktop
person Matt Johnson-Pint    schedule 21.06.2016
comment
Какой API использует эта библиотека внутри компании? Вызывает ли он функции Win32 true -async (перекрывающийся ввод-вывод?) Или просто оборачивает все в поток? - person Dai; 03.11.2017
comment
Это правильный ответ, потому что он не имитирует асинхронность с потоками, а фактически подключается к реальным прерываниям в аппаратном / программном обеспечении. - person PRMan; 03.09.2020
comment
Не похоже, что UWP для настольных ПК выдержала испытание временем. Вероятно, есть более современные способы p / вызова API-интерфейсов UWP. Может быть, .NET 5 поможет. :) - person Matt Johnson-Pint; 04.09.2020

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


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

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

person moswald    schedule 26.01.2014
comment
Не совсем. Даже возврат первого файла может занять десятки или сотни миллисекунд (подумайте о путях UNC). Все это время ваш поток выполнения заблокирован. - person MvdD; 24.11.2014
comment
Перечисление драйва тоже более 5 минут. - person Justin; 01.03.2021

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

person Jörgen Sigvardsson    schedule 01.01.2013
comment
Это ленивый, а не асинхронный - person Mauricio Scheffer; 02.01.2013
comment
Ну засуньте это в асинхронный метод / Task, что у вас. Простое перетаскивание всего в другой поток - плохой способ добиться асинхронности. ИМХО. Да, он работает параллельно, но его очень сложно завершить детерминированным образом. - person Jörgen Sigvardsson; 02.01.2013
comment
Вставка этого в асинхронный метод / задачу - это не одно и то же, и это не связано с ленью. Я согласен с тем, что толкать его в другой поток - плохой асинхронный режим, он должен использовать IOCP. - person Mauricio Scheffer; 02.01.2013
comment
Удивительно, что несколько ответов привели к такому решению, хотя оно не имеет ничего общего с решением заявленной проблемы. Также интересно, что неправильный ответ C # намного длиннее, чем эквивалентный неправильный ответ в F # :-). - person James Moore; 02.01.2013

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

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

Я надеялся, что существует какой-то эквивалент для _2 _ / _ 3_ для каталогов, и этот _4 _ / _ 5_ был просто хорошей оболочкой для вызовов нижнего уровня, которая раскрывала некоторые варианты асинхронности. Что-то вроде _6 _ / _ 7_.

person James Moore    schedule 05.04.2009
comment
Я не мог найти ответа принцессы. - person Scott Hutchinson; 31.08.2017
comment
@ScottHutchinson Его больше нет, не знаю, когда его удалили. (Мой вопрос от ‹gulp› 2009 г.). Я думаю, что это было по сути то же самое, что и у Джульетты, указав, что многие асинхронные операторы в любом случае не имеют хорошей поддержки - они собираются потреблять потоки и не делать что-то разумное с дескрипторами ожидания. - person James Moore; 01.09.2017
comment
Я предполагаю, что принцесса сменила имя на Джульетта, в профиле которой написано, что она Верховная жрица, что в некотором роде похоже на принцессу. - person Scott Hutchinson; 01.09.2017
comment
Нет, верховная жрица - это религиозный титул, а принцесса - член королевского двора, что ... эй, мы совсем не по теме! - person Bent Tranberg; 21.06.2021