异步Azure文件存储 - 上载任务等待其他任务

时间:2017-06-29 20:54:12

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

我有这个代码以异步方式运行委托:

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个文件相同。

1 个答案:

答案 0 :(得分:1)

  

如您所见,在任何上传任务完成之前,所有10个任务都必须先进入等待状态?我的问题是为什么?这与5,10,100或1000个文件相同。

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

第一个。您上传的文件不会超过线程池编号。

由于每个BeginInvoke操作都会在线程池中选择一个线程来执行UploadFile方法(所有操作都是异步)并等待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);
        });