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

C# 异步Azure文件存储-上载任务等待其他任务,c#,azure,async-await,file-storage,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

我有一段异步运行委托的代码:

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

如您所见,所有10个任务都必须先进入等待状态,然后才能完成任何上载任务?我的问题是为什么?这与5、10、100或1000个文件相同

在我看来,我认为这个手术应该分为两种情况

第一个。上载的文件未超过线程池编号

由于每个BeginInvoke操作都会在线程池中选择一个线程来执行UploadFile方法(所有操作都是异步的)并等待TransferManager.UploadAsync将花费大量时间来上载文件,因此您会发现,似乎等待所有任务完成后再开始上载

事实上,一个线程不会影响另一个线程,只是该线程的执行速度非常快

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

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

因此,您会发现应用程序首先创建足够的线程来运行UploadFile,就像它先等待一样。在我看来,没有足够的线程来执行await TransferManager.UploadAsync方法(这将从线程池中获得另一个线程来运行它)。因为正在运行的上载线程的优先级高于等待TransferManager.UploadAsync线程。因此,计算机将首先创建新线程来运行UploadFile方法。如果所有UploadFile方法都完全运行,那么它将分配另一个线程来运行TransferManager.UploadAsync方法

如果希望应用程序不等待所有任务,可以尝试使用并行类

更多详细信息,您可以参考以下代码:

        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);
        });

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