我有这个代码以异步方式运行委托:
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个文件相同。
答案 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);
});