【问题标题】:Async Azure File Storage - Upload Tasks Wait For Other Tasks异步 Azure 文件存储 - 上传任务等待其他任务
【发布时间】:2017-06-29 20:54:11
【问题描述】:

我有这个异步运行委托的代码:

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 个文件都是一样的。

【问题讨论】:

  • 在我看来,这与线程池大小有关。由于 BeginInvoke 和 await 方法都将使用线程池的线程。进程的线程池的默认大小取决于几个因素,例如虚拟地址空间的大小。线程数超过了当前线程池的线程。它不会在所有情况下都立即创建新线程。如果有未完成的任务,它会每 0.5 秒创建一个线程,直到最大线程数。所以没有足够的线程来执行 await TransferManager.UploadAsync 方法。

标签: c# azure async-await file-storage


【解决方案1】:

如您所见,所有 10 个任务都必须先进入等待,然后才能完成任何上传任务?我的问题是为什么?这对于 5、10、100 或 1000 个文件都是一样的。

在我看来,我认为这个操作应该分为两种情况。

第一个。你上传的文件不超过线程池数。

由于每个BeginInvoke操作都会在线程池中选择一个线程来执行UploadFile方法(所有操作都是异步的),而await TransferManager.UploadAsync上传文件会花费很多时间,所以你会发现,看来要等所有任务完成后再开始上传。

实际上一个线程不会影响另一个线程,只是线程执行得非常快。

其次,如果应用程序使用的线程数超过当前线程池数。

进程的线程池的默认大小取决于几个因素,例如虚拟地址空间的大小。线程数超过了当前线程池的线程。它不会在所有情况下立即创建新线程。如果有未完成的任务,它会每 0.5 秒创建一个线程,直至最大线程数。

所以你会发现应用程序首先创建了足够的线程来运行 UploadFile 看起来它首先等待。在我看来,没有足够的线程来执行 await TransferManager.UploadAsync 方法(这将从线程池中获取另一个线程运行它)。由于正在运行的上传线程比等待 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);
        });

【讨论】:

    猜你喜欢
    • 2013-05-26
    • 2019-04-08
    • 2014-03-18
    • 1970-01-01
    • 2013-02-10
    • 2017-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多