De façon asynchrone et en parallèle le téléchargement de fichiers

MODIFIER

J'ai changé le titre de la question afin de refléter le problème que j'ai eu mais aussi une réponse sur la façon de réaliser cela facilement.


Je suis en train de faire la 2ème méthode pour retourner Task<TResult> au lieu de Task comme dans la méthode 1, mais je suis une cascade d'erreurs comme une conséquence de tenter d'y remédier.

  • J'ai ajouté return avant await body(partition.Current);
  • À son tour, il me demande d'ajouter une instruction de retour ci-dessous j'ai donc ajouté return null ci-dessous
  • Mais maintenant, l'instruction select se plaint qu'il ne peut pas déduire le type de l'argument de la requête
  • - Je changer Task.Run à Task.Run<TResult> mais sans succès.

Comment puis-je résoudre ce problème ?

La première méthode vient de http://blogs.msdn.com/b/pfxteam/archive/2012/03/05/10278165.aspx, la deuxième méthode est la surcharge de travail que je suis en train de créer.

public static class Extensions
{
    public static Task ForEachAsync<T>(this IEnumerable<T> source, int dop, Func<T, Task> body)
    {
        return Task.WhenAll(
            from partition in Partitioner.Create(source).GetPartitions(dop)
            select Task.Run(async delegate
            {
                using (partition)
                    while (partition.MoveNext())
                        await body(partition.Current);
            }));
    }

    public static Task ForEachAsync<T, TResult>(this IEnumerable<T> source, int dop, Func<T, Task<TResult>> body)
    {
        return Task.WhenAll(
            from partition in Partitioner.Create(source).GetPartitions(dop)
            select Task.Run(async delegate
            {
                using (partition)
                    while (partition.MoveNext())
                        await body(partition.Current);
            }));
    }
}

Exemple d'utilisation :

Avec cette méthode, je voudrais télécharger plusieurs fichiers en parallèle et de manière asynchrone :

private async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Artist artist = await GetArtist();
IEnumerable<string> enumerable = artist.Reviews.Select(s => s.ImageUrl);
string[] downloadFile = await DownloadFiles(enumerable);
}
public static async Task<string[]> DownloadFiles(IEnumerable<string> enumerable)
{
if (enumerable == null) throw new ArgumentNullException("enumerable");
await enumerable.ForEachAsync(5, s => DownloadFile(s));
//Incomplete, the above statement is void and can't be returned
}
public static async Task<string> DownloadFile(string address)
{
/* Download a file from specified address, 
* return destination file name on success or null on failure */
if (address == null)
{
return null;
}
Uri result;
if (!Uri.TryCreate(address, UriKind.Absolute, out result))
{
Debug.WriteLine(string.Format("Couldn't create URI from specified address: {0}", address));
return null;
}
try
{
using (var client = new WebClient())
{
string fileName = Path.GetTempFileName();
await client.DownloadFileTaskAsync(address, fileName);
Debug.WriteLine(string.Format("Downloaded file saved to: {0} ({1})", fileName, address));
return fileName;
}
}
catch (WebException webException)
{
Debug.WriteLine(string.Format("Couldn't download file from specified address: {0}", webException.Message));
return null;
}
}
Il n'est pas clair du tout ce que vous attendez le résultat. Vous êtes de passage dans une séquence entière de T valeurs, et l'exécution de la même fonction sur les deux - ce qu'un seul résultat attendez-vous pour sortir de la Task<TResult> retourné?
Je voudrais obtenir une Tâche<string> dans ce cas, j'ai ajouté un exemple sur ma question.
"Avec cette méthode, je voudrais télécharger plusieurs fichiers en parallèle et asynchrone" : Parallel.Foreach n'est-ce pas assez?
voudriez-vous être un Task<IEnumerable<string>> dans votre cas, ou quelle chaîne voulez-vous de retour si vraiment vous ne voulez Task<string>?
Je pense que vous ne comprenez toujours pas. Imaginez que vous êtes le téléchargement de deux pages, l'une contenant foo et les autres bar. Si votre ForEachAsync() était de retour Task<string>, quelle chaîne voulez-vous qu'il contient? Compte tenu de votre code, il ferait beaucoup plus de sens que s'il est retourné Task<string[]>.

OriginalL'auteur Aybe | 2013-10-04