#for-loop #async-await #asp.net-core-mvc #visual-studio-2019
#for-loop #async-await #asp.net-core-mvc #visual-studio-2019
Вопрос:
Я пытаюсь увеличить скорость загрузки файлов в моем приложении, загружая их параллельно. Раньше я загружал их последовательно, и все работало нормально, но когда я попытался загрузить их параллельно, я столкнулся с необъяснимыми проблемами.
Вот мой метод, с помощью которого я последовательно загружал файлы:
public IActionResult DownloadPartFiles([FromBody] FileRequestParameters parameters)
{
List<InMemoryFile> files = new List<InMemoryFile>();
for (int i = 0; i < parameters.FileNames.Length; i )
{
InMemoryFile inMemoryFile = GetInMemoryFile(parameters.FileLocations[i], parameters.FileNames[i]).Resu<
files.Add(inMemoryFile);
}
byte[] archiveFile = null;
using (MemoryStream archiveStream = new MemoryStream())
{
using (ZipArchive archive = new ZipArchive(archiveStream, ZipArchiveMode.Create, true))
{
foreach (InMemoryFile file in files)
{
ZipArchiveEntry zipArchiveEntry = archive.CreateEntry(file.FileName, CompressionLevel.Optimal);
using (MemoryStream originalFileStream = new MemoryStream(file.Content))
using (Stream zipStream = zipArchiveEntry.Open())
{
originalFileStream.CopyTo(zipStream);
}
}
}
archiveFile = archiveStream.ToArray();
}
return File(archiveFile, "application/octet-stream");
}
Вот метод, измененный для параллельной загрузки файлов:
public async Task<IActionResult> DownloadPartFiles([FromBody] FileRequestParameters parameters)
{
List<Task<InMemoryFile>> fileTasks = new List<Task<InMemoryFile>>();
for (int i = 0; i < parameters.FileNames.Length; i )
{
if(i == parameters.FileNames.Length - 1)
{
int breakpoint = 0;
}
if(i == parameters.FileNames.Length)
{
int breakpoint = 0;
}
fileTasks.Add(Task.Run(() => GetInMemoryFile(parameters.FileLocations[i], parameters.FileNames[i])));
}
InMemoryFile[] fileResults = await Task.WhenAll(fileTasks);
byte[] archiveFile = null;
using (MemoryStream archiveStream = new MemoryStream())
{
using (ZipArchive archive = new ZipArchive(archiveStream, ZipArchiveMode.Create, true))
{
foreach (InMemoryFile file in fileResults)
{
ZipArchiveEntry zipArchiveEntry = archive.CreateEntry(file.FileName, CompressionLevel.Optimal);
using (MemoryStream originalFileStream = new MemoryStream(file.Content))
using (Stream zipStream = zipArchiveEntry.Open())
{
originalFileStream.CopyTo(zipStream);
}
}
}
archiveFile = archiveStream.ToArray();
}
return File(archiveFile, "application/octet-stream");
}
Вот метод, который выполняет фактическую загрузку:
private async Task<InMemoryFile> GetInMemoryFile(string fileLocation, string fileName)
{
InMemoryFile file;
using (HttpClient client = new HttpClient())
using (HttpResponseMessage response = await client.GetAsync(fileLocation))
{
byte[] fileContent = await response.Content.ReadAsByteArrayAsync();
file = new InMemoryFile(fileName, fileContent);
}
return file;
}
Теперь проблемы, с которыми я сталкиваюсь, заключаются в том, что после того, как я изменил DownloadPartFiles, чтобы получать все файлы параллельно, мой цикл for теперь, похоже, выходит за пределы своего состояния остановки. Например, параметры if.Имена файлов.Длина возвращает 12 цикл for не должен выполняться, когда i = 12, и он должен выйти из цикла. Однако в моем тестировании он будет продолжать выполняться при i = 12, и, как и следовало ожидать, я столкнулся с ошибкой out of bounds. Я попытался установить точки останова в своем коде, чтобы убедиться, что он действительно выполняется после условия остановки, и возникло более странное поведение. В мой цикл for я включил два оператора if с переменными точки останова для прерывания. Он всегда будет прерываться, когда я должен быть в его последнем цикле, но никогда не прерывается, когда я один после ожидаемого последнего цикла. Кажется, что эта точка останова пропускается, когда я превышаю ожидаемое условие остановки. Он будет работать нормально, если я пройдусь по коду во время отладки, но выйдет за пределы ошибки, когда я позволю ему работать нормально.
Я не уверен, почему это происходит, но я все еще новичок в асинхронном программировании, так что, возможно, это просто недосмотр где-то. Дайте мне знать, если мне нужно что-нибудь еще объяснить.
Комментарии:
1. Насколько вы в этом уверены
parameters.FileLocations.Length == parameters.FileNames.Length
?2. Да, я действительно обнаружил проблему и опубликую ответ.
Ответ №1:
Я совершаю критическую ошибку, поскольку я попытался обернуть асинхронный метод (мой метод GetInMemoryFile) в метод Task.Run(), который используется для обертывания синхронных методов, чтобы заставить их работать асинхронно. Это вызвало странное поведение.
Короче говоря, я изменил
fileTasks.Add(Task.Run(() => GetInMemoryFile(parameters.FileLocations[i], parameters.FileNames[i])));
Для
fileTasks.Add(GetInMemoryFile(parameters.FileLocations[i], parameters.FileNames[i]));