2016-05-16 10 views
3

Mein Ziel ist es, Bilder aus einem Amazon Web Services-Bucket herunterzuladen.C# Task.WaitAll wartet nicht

ich den folgenden Code Funktion haben, die mehrere Bilder auf einmal herunterlädt:

public static void DownloadFilesFromAWS(string bucketName, List<string> imageNames) 
{ 
    int batchSize = 50; 
    int maxDownloadMilliseconds = 10000; 

    List<Task> tasks = new List<Task>(); 

    for (int i = 0; i < imageNames.Count; i++) 
    { 
     string imageName = imageNames[i]; 
     Task task = Task.Run(() => GetFile(bucketName, imageName)); 
     tasks.Add(task); 
     if (tasks.Count > 0 && tasks.Count % batchSize == 0) 
     { 
      Task.WaitAll(tasks.ToArray(), maxDownloadMilliseconds);//wait to download 
      tasks.Clear(); 
     } 
    } 

    //if there are any left, wait for them 
    Task.WaitAll(tasks.ToArray(), maxDownloadMilliseconds); 
} 

private static void GetFile(string bucketName, string filename) 
{ 
    try 
    { 
     using (AmazonS3Client awsClient = new AmazonS3Client(Amazon.RegionEndpoint.EUWest1)) 
     { 
      string key = Path.GetFileName(filename); 

      GetObjectRequest getObjectRequest = new GetObjectRequest() { 
       BucketName = bucketName, 
        Key = key 
      }; 

      using (GetObjectResponse response = awsClient.GetObject(getObjectRequest)) 
      { 
       string directory = Path.GetDirectoryName(filename); 
       if (!Directory.Exists(directory)) 
       { 
        Directory.CreateDirectory(directory); 
       } 

       if (!File.Exists(filename)) 
       { 
        response.WriteResponseStreamToFile(filename); 
       } 
      } 
     } 
    } 
    catch (AmazonS3Exception amazonS3Exception) 
    { 
     if (amazonS3Exception.ErrorCode == "NoSuchKey") 
     { 
      return; 
     } 
     if (amazonS3Exception.ErrorCode != null && (amazonS3Exception.ErrorCode.Equals("InvalidAccessKeyId") || amazonS3Exception.ErrorCode.Equals("InvalidSecurity"))) 
     { 
      // Log AWS invalid credentials 
      throw new ApplicationException("AWS Invalid Credentials"); 
     } 
     else 
     { 
      // Log generic AWS exception 
      throw new ApplicationException("AWS Exception: " + amazonS3Exception.Message); 
     } 
    } 
    catch 
    { 
     // 
    } 
} 

Das Herunterladen der Bilder funktioniert alles gut, aber die Task.WaitAll scheint weiterhin ignoriert, und der Rest des Codes werden ausgeführt werden - Das heißt, ich versuche, Dateien zu erhalten, die derzeit nicht vorhanden sind (da sie noch nicht heruntergeladen wurden).

Ich fand this Antwort auf eine andere Frage, die die gleiche wie meine scheint. Ich versuchte, die Antwort zu verwenden, um meinen Code zu ändern, aber es würde immer noch nicht auf das Herunterladen aller Dateien warten.

Kann mir jemand sagen, wo ich falsch liege?

+0

Sollten Sie nicht zuerst alle Download-Aufgaben hinzufügen und nur * * danach ** warte auf den Abschluss? So mache ich es. Die Art, wie Sie vorgehen - Sie warten sofort, nachdem Sie einen neuen Thread in den Pool gestellt haben, um ihn zu vervollständigen - sieht wie eine synchrone Programmierung aus - mit anderen Worten, Sie profitieren nicht von Multithreading. Dies bezieht sich nicht direkt auf * nicht warten *, sondern ... als Bemerkung. – Veverke

+0

@Veverke Ich bin neu in Tasks, also dachte ich, wenn ich sie alle hinzufügen würde, nehme an, es gäbe 10.000 Dateien, die ich herunterladen müsste, würden alle Aufgaben gleichzeitig laufen und zu viel für das System sein ??? - Aus diesem Grund beschließe ich, die Aufgaben in Stapeln auszuführen. – Rick

+0

Nun, ich vermisste die Tatsache, dass Sie 2 warten, ich sah nur die erste. Ich frage dann etwas anderes: Warum brauchst du das in der Schleife? Hast du einmal versucht ohne es zu laufen? Versuchen Sie, das if in der for-Schleife zu kommentieren und sehen Sie, was passiert. Erhalten Sie Ausnahmen? – Veverke

Antwort

5

Der Code verhält sich wie erwartet. Task.WaitAll kehrt nach zehn Sekunden zurück, auch wenn nicht alle Dateien heruntergeladen wurden, weil Sie in Variable maxDownloadMilliseconds eine Zeitüberschreitung von 10 Sekunden (10000 Millisekunden) angegeben haben.

Wenn Sie wirklich warten möchten, bis alle Downloads abgeschlossen sind, rufen Sie Task.WaitAll an, ohne ein Zeitlimit anzugeben.

Verwenden

Task.WaitAll(tasks.ToArray());//wait to download 

beiden Orten an.

Um einige gute Erklärungen zu sehen, wie parallele Downloads zu implementieren, während das System nicht betont (nur eine maximale Anzahl von parallelen Downloads haben) finden Sie in der Antwort auf How can I limit Parallel.ForEach?

+0

Hmmmm ... ich nach 10 Sekunden angenommen, die unerledigten Aufgaben abgebrochen werden würden ... Let me test ... – Rick

+1

Die nicht beendeten Aufgaben werden nicht abgebrochen, der WaitAll kehrt nur zurück, während die Aufgaben im Hintergrund weiterlaufen – NineBerry

+1

Wenn Sie wollen, dass die Aufgaben abgebrochen werden, müssen Sie ein CancellationToken innerhalb dieses Spiels mitbringen: o) –