Я работал над рефакторингом процесса, который выполняет итерацию по коллекции FileClass
объектов, имеющих свойства Filename
, NewFilename
и string[] FileReferences
, и заменяет все FileReferences
, в которых ссылаются на старое имя файла с новым. Приведенный ниже код несколько упрощен тем, что реальное свойство ссылок на файлы - это не просто список имен файлов - это строки, которые могут содержать имя файла где-то в них или нет. Текущий код в порядке, если в коллекции _fileClass
меньше 1000 объектов ... но он мучительно медленный, если есть больше объектов или свойство ссылок на файлы имеет тысячи.
После ответов на этот пост: Запустите две асинхронные c задачи параллельно и соберите результаты в. NET 4.5 (и нескольким нравится). Я пытался создать asyn c метод, который бы брал список всех старых и новых имен файлов, а также отдельный FileClass
, затем создавал массив из этих Task<FileClass>
и пытался обработать их в параллельно через Task.WhenAll()
. Но возникает ошибка «Cannot a awit void». Я считаю, что это связано с Task.Run(() => ...);
, но удаление () =>
вызывает дальнейшие проблемы.
Это более старая кодовая база, и я не могу позволить асину c распространяться дальше, чем вызывающий код (в данном случае Main
, как я нашел в некоторых других примерах. Также я не могу используйте asyn c foreach в C # 8 из-за ограничения. Net 4.5.
class Program
{
private static List<FileClass> _fileClasses;
static void Main(string[] args)
{
var watch = new Stopwatch();
_fileClasses = GetFileClasses();
watch.Start();
ReplaceFileNamesAsync();
watch.Stop();
Console.WriteLine($"Async Elapsed Ticks: {watch.ElapsedTicks}");
watch.Reset();
//watch.Start();
//ReplaceFileNamesSLOW();
//watch.Stop();
//Console.WriteLine($"Slow Elapsed Ticks: {watch.ElapsedTicks}");
Console.ReadLine();
}
public static async void ReplaceFileNamesAsync()
{
var newOldFilePairs = _fileClasses.Select(p => new NewOldFilePair() { OldFile = p.Filename, NewFile = p.NewFilename }).ToArray();
var tasks = new List<Task<FileClass>>();
foreach (var file in _fileClasses)
{
tasks.Add(ReplaceFileNamesAsync(newOldFilePairs, file));
}
//Red underline "Cannot await void".
FileClass[] result = await Task.WaitAll(tasks.ToArray());
}
private static async Task<FileClass> ReplaceFileNamesAsync(NewOldFilePair[] fastConfigs, FileClass fileClass)
{
foreach (var config in fastConfigs)
{
//I suspect this is part of the issue.
await Task.Run(() => fileClass.ReplaceFileNamesInFileReferences(config.OldFile, config.NewFile));
}
return fileClass;
}
public static void ReplaceFileNamesSLOW()
{
// Current Technique
for (var i = 0; i < _fileClasses.Count; i++)
{
var oldName = _fileClasses[i].Filename;
var newName = _fileClasses[i].NewFilename;
for (var j = 0; j < _fileClasses.Count; j++)
{
_fileClasses[j].ReplaceFileNamesInFileReferences(oldName, newName);
}
}
}
public static List<FileClass> GetFileClasses(int numberToGet = 2000)
{
//helper method to build a bunch of FileClasses
var fileClasses = new List<FileClass>();
for (int i = 0; i < numberToGet; i++)
{
fileClasses.Add(new FileClass()
{
Filename = $@"C:\fake folder\fake file_{i}.ext",
NewFilename = $@"C:\some location\sub folder\fake file_{i}.ext"
});
}
var fileReferences = fileClasses.Select(p => p.Filename).ToArray();
foreach (var fileClass in fileClasses)
{
fileClass.FileReferences = fileReferences;
}
return fileClasses;
}
}
public class NewOldFilePair
{
public string OldFile { get; set; }
public string NewFile { get; set; }
}
public class FileClass
{
public string Filename { get; set; }
public string NewFilename { get; set; }
public string[] FileReferences { get; set; }
//Or this might be the void it doesn't like.
public void ReplaceFileNamesInFileReferences(string oldName, string newName)
{
if (FileReferences == null) return;
if (FileReferences.Length == 0) return;
for (var i = 0; i < FileReferences.Length; i++)
{
if (FileReferences[i] == oldName) FileReferences[i] = newName;
}
}
}
Обновление Если другие найдут этот вопрос и действительно будут нуждаться в реализации чего-то подобного выше, Были некоторые потенциальные подводные камни, о которых стоит упомянуть. Очевидно, у меня была опечатка для Task.WaitAll()
против Task.WhenAll()
(я обвиняю VS в автозаполнении, и, возможно, я тороплюсь сделать скретч-приложение ?). Во-вторых, как только код «заработал», Я обнаружил, что в то время как asyn c сокращал время, чтобы пройти через это, он не завершал весь список задач (как они могут быть в тысячах), прежде чем перейти к следующему этапу процесса. вызов Task.Run(() => ReplaceFileNamesAsync()).Wait()
, который на самом деле занял больше времени, чем вложенный метод l oop. Для распаковки и объединения результатов обратно в свойство _fileClasses
также потребовалось несколько логических операций c, что способствовало
* Parallel.ForEach
был гораздо более быстрым процессом, и хотя я не увидел обновленный код, опубликованный ниже, я получил в основном тот же результат (кроме словаря).