Асинхронное хранилище файлов Azure — задачи загрузки ждут других задач

У меня есть этот код, который запускает делегат асинхронно:

public delegate void FileHandler(string path);
....
FileHandler fileHandler = HandleFile;
foreach (FileInfo file in files)
{
    string filePath = file.FullName;
    if (IO.FileAvailable(filePath))
    {
        fileHandler.BeginInvoke(filePath, null, null); // i call EndInvoke later
    }
}

Вот делегат, который просто загружает данные в хранилище файлов Azure:

private static void HandleFile(string path)
{
    AzureStorage.Instance.UploadFile("some-key", path);
}

Кроме того, это функции загрузки:

public async Task UploadFileAsync(string storageKey, string filePath)
{
    CloudFile loc = Navigate(storageKey);
    await TransferManager.UploadAsync(filePath, loc);
}

public void UploadFile(string storageKey, string filePath)
{
    Task uploadTask = UploadFileAsync(storageKey, filePath);
    Console.WriteLine("Will wait for " + storageKey + " path: " + filePath + " with thread: " + Thread.CurrentThread.ManagedThreadId);
    uploadTask.Wait();
    Console.WriteLine(">>> Done waiting for " + storageKey + " path: " + filePath);
    uploadTask.Dispose();
}

Вот мой результат:

Will wait for /bills/test/SomePdfName_1.pdf path: C:\test\SomePdfName_1.pdf with thread: 4
Will wait for /bills/test/SomePdfName_6.pdf path: C:\test\SomePdfName_6.pdf with thread: 7
Will wait for /bills/test/SomePdfName_10.pdf path: C:\test\SomePdfName_10.pdf with thread: 5
Will wait for /bills/test/SomePdfName_5.pdf path: C:\test\SomePdfName_5.pdf with thread: 9
Will wait for /bills/test/SomePdfName_3.pdf path: C:\test\SomePdfName_3.pdf with thread: 8
Will wait for /bills/test/SomePdfName_7.pdf path: C:\test\SomePdfName_7.pdf with thread: 11
Will wait for /bills/test/SomePdfName_4.pdf path: C:\test\SomePdfName_4.pdf with thread: 10
Will wait for /bills/test/SomePdfName_2.pdf path: C:\test\SomePdfName_2.pdf with thread: 6
Will wait for /bills/test/SomePdfName_8.pdf path: C:\test\SomePdfName_8.pdf with thread: 12
Will wait for /bills/test/SomePdfName_9.pdf path: C:\test\SomePdfName_9.pdf with thread: 13
.... 10 seconds later
>>> Done waiting for /bills/test/SomePdfName_9.pdf path: C:\test\SomePdfName_9.pdf
>>> Done waiting for /bills/test/SomePdfName_10.pdf path: C:\test\SomePdfName_10.pdf
>>> Done waiting for /bills/test/SomePdfName_1.pdf path: C:\test\SomePdfName_1.pdf
>>> Done waiting for /bills/test/SomePdfName_7.pdf path: C:\test\SomePdfName_7.pdf
>>> Done waiting for /bills/test/SomePdfName_2.pdf path: C:\test\SomePdfName_2.pdf
>>> Done waiting for /bills/test/SomePdfName_5.pdf path: C:\test\SomePdfName_5.pdf
>>> Done waiting for /bills/test/SomePdfName_6.pdf path: C:\test\SomePdfName_6.pdf
>>> Done waiting for /bills/test/SomePdfName_8.pdf path: C:\test\SomePdfName_8.pdf
>>> Done waiting for /bills/test/SomePdfName_3.pdf path: C:\test\SomePdfName_3.pdf
>>> Done waiting for /bills/test/SomePdfName_4.pdf path: C:\test\SomePdfName_4.pdf

Как вы видите, все 10 задач должны сначала войти в состояние ожидания, прежде чем любая из задач загрузки завершится? Мой вопрос, почему это? То же самое и с 5, 10, 100 или 1000 файлами.


person jn1kk    schedule 29.06.2017    source источник
comment
На мой взгляд, это связано с размером пула потоков. Поскольку оба метода BeginInvoke и await будут использовать поток пула потоков. Размер пула потоков по умолчанию для процесса зависит от нескольких факторов, таких как размер виртуального адресного пространства. Количество потоков превышает поток текущего пула потоков. Он не создает новые потоки сразу во всех ситуациях. Он будет создавать один поток каждые 0,5 секунды, если есть невыполненные задачи, вплоть до максимального количества потоков. Поэтому не хватает потоков для выполнения метода await TransferManager.UploadAsync.   -  person Brando Zhang    schedule 30.06.2017


Ответы (1)


Как вы видите, все 10 задач должны сначала войти в состояние ожидания, прежде чем любая из задач загрузки завершится? Мой вопрос почему так? То же самое и с 5, 10, 100 или 1000 файлами.

На мой взгляд, я думаю, что эту операцию следует разделить на два случая.

Первый. Загружаемый файл не превышает номер пула потоков.

Поскольку каждая из операций BeginInvoke будет выбирать один поток в пуле потоков для выполнения метода UploadFile (все операции являются асинхронными) и await TransferManager.UploadAsync потребуется много времени для загрузки файла, поэтому вы обнаружите, что он выглядит например, дождитесь завершения всей задачи, затем начните загрузку.

На самом деле один поток не повлияет на другой поток, просто поток выполняется очень быстро.

Во-вторых, если приложение использует номер потока, превышающий текущий номер пула потоков.

Размер пула потоков по умолчанию для процесса зависит от нескольких факторов, таких как размер виртуального адресного пространства. Количество потоков превышает поток текущего пула потоков. Он не создает новые потоки сразу во всех ситуациях. Он будет создавать один поток каждые 0,5 секунды, если есть невыполненные задачи, вплоть до максимального количества потоков.

Таким образом, вы обнаружите, что приложение сначала создает достаточно потока для запуска UploadFile, похоже, что оно ждет сначала. На мой взгляд, недостаточно потоков для выполнения метода await TransferManager.UploadAsync (это получит другой поток из пула потоков для его запуска ). Поскольку текущий поток загрузки имеет более высокий приоритет, чем поток await TransferManager.UploadAsync. Таким образом, компьютер сначала создаст новый поток для запуска метода UploadFile. Если все методы UploadFile выполняются полностью, то будет назначен другой поток для выполнения метода TransferManager.UploadAsync.

Если вы хотите, чтобы ваше приложение не ждало выполнения всей задачи, вы можете попробовать использовать класс Parallel.

Подробнее, вы можете обратиться к этим кодам:

        Parallel.ForEach(dirInfo.GetFiles(), new ParallelOptions() { MaxDegreeOfParallelism = 108 }, (file) =>
        {
            string filePath = file.FullName;

            Console.WriteLine("start BeginInvoke for file: " + file + " with thread: " + Thread.CurrentThread.ManagedThreadId);
            fileHandler.Invoke(filePath); // i call EndInvoke later

            Console.WriteLine("end BeginInvoke for file: " + file + " with thread: " + Thread.CurrentThread.ManagedThreadId);
        });
person Brando Zhang    schedule 04.07.2017