Существует ли асинхронная версия DirectoryInfo.GetFiles / Directory.GetDirectories в dotNet?
-
23-08-2019 - |
Вопрос
Существует ли асинхронная версия 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)
Возможно, смотрите также
http://weblogs.asp.net/podwysocki/archive/2009/03/18/functional-net-laziness-becomes-you.aspx
Я не программист на 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
.