2017-06-29 53 views
0

我有這樣的代碼異步運行的委託:異步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個文件相同。

+0

在我看來,這是與線程池的大小有關。由於 BeginInvoke和await方法都將使用線程池的線程。進程的線程池的缺省大小取決於幾個因素,例如虛擬地址空間的大小。線程的數量超過當前線程池的線程。它不會立即在所有情況下創建新線程。如果有未完成的任務,它將每0.5秒創建一個線程,最大線程數最多。所以沒有足夠的線程來執行await TransferManager.UploadAsync方法。 –

回答

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