7
votes

Task.Factory.ContinueWhenany Continuer quand toute tâche se termine sans exception

J'ai 3 tâches dans ma demande qui sont responsables de l'obtention de données à partir de bases de données.
Jusqu'à présent, j'ai eu toutes les tâches exécutées une après une. Si vous avez terminé d'abord et que c'était en résultat, ceci a été mes données, si maintenant, j'ai commencé la deuxième tâche et j'ai vérifié à nouveau.

Récemment, j'ai trouvé des informations que je peux commencer plusieurs tâches et continuer lorsque l'une d'entre elles a fini d'utiliser l'utilisation de la tâche .ContinueWhenany code>. Cela fonctionne bien si toute ma tâche ne jette aucune exception, mais si une tâche échoue, je ne peux pas obtenir de résultats que je veux. P>

Par exemple: P>

var t1 = Task.Factory.StartNew(() =>
{
    var rnd = new Random();
    Thread.Sleep(rnd.Next(5,15)*1000);
    throw new Exception("My error");
    return 1;
});

var t2 = Task.Factory.StartNew(() =>
{
    Thread.Sleep(2000);
    throw new Exception("My error");
    return 2;
});

var t3 = Task.Factory.StartNew(() =>
{
    throw new Exception("My error");
    return 3;
});

var tasks = new List<Task<int>> { t1, t2, t3 };

Action<Task<int>> handler = null;

handler = t =>
{
    if (t.IsFaulted)
    {
        tasks.Remove(t);
        if (tasks.Count == 0)
        {
            throw new Exception("No data at all!");
        }
        Task.Factory.ContinueWhenAny(tasks.ToArray(), handler);
    }
    else
    {
        Console.WriteLine(t.Result);
    }
};

Task.Factory.ContinueWhenAny(tasks.ToArray(), handler);


1 commentaires

Je pense que vous devez écrire votre propre tâche. Qui est la version qui prend une tâche Func Spécifie si cette opération de tâche est censée compléter l'onduleur.


3 Réponses :


3
votes

Il y a une surcharge sur le ContinuationWhenany code> fonction qui fait ce que vous voulez.

Définissez simplement la tâche TaskContatinaterOptions code> sur OnlyonRantocalistion code> et les tâches ayant échoué seront ignorées. P >

var tasks = new List<Task> {t1, t2, t3};
while (tasks.Any()) 
{
    var finishedTask = await Task.WhenAny(tasks);
    if (finishedTask.IsFaulted)
    {
        tasks.Remove(finishedTask);
    }
    else
    {
        var result = await finishedTask;
        Console.WriteLine(result);
        return;
    }
}


12 commentaires

Merci d'une telle réponse rapide, mais malheureusement, je reçois une erreur , il n'est pas valide pour exclure des types de continuation spécifiques pour la poursuite de plusieurs tâches.


Ah ... cela arrive si vous ne lisez pas correctement la documentation. Désolé, cette réponse est fausse. Parce que les options noton * et seule * sont illégales pour cette fonction. Comme c'est la seule idée que j'ai obtenu est de conserver une liste des tâches que vous attendez et si la fonction déclenche une tâche échouée, vous pouvez supprimer celui-ci et attendre le reste.


Pourriez-vous montrer un exemple de code? Ce serait très utile.


Y a-t-il une raison explicite que vous souhaitez utiliser ce schéma? Ou envisagez-vous d'utiliser le modèle ASYNC-Await?


@Misiu J'ai ajouté du code à la réponse qui peut vous donner une idée de la façon de le résoudre. J'ai écrit cela en 2 minutes et je ne suis pas très bon avec C #. Alors s'il vous plaît exécuter des erreurs là-bas. Je suis plus d'une personne VB.


Le nouveau code est racé car plusieurs gestionnaires peuvent terminer en même temps. En outre, il a des performances quadratiques.


@usr tu es sûr? Je ne pense pas que plusieurs gestionnaires sont actifs en même temps. Parce que continue quand ne tire qu'une seule fois, dès qu'il rencontre une tâche terminée et que pour une tâche. Donc, la seule continueWhenany ne devrait pas entraîner plus d'un appel du Continuation


@Nitram Je n'ai aucune raison de ce schéma, je pourrais utiliser ASYNC Await, mais parce que je commence par ces choses que je ne sais pas comment les utiliser.


@Misiu utilisez-vous .NET 4.5 ou .NET 4.0?


@Misiu maintenant cette réponse est lentement un peu longue. Je l'ai édité pour fournir un exemple d'ASYNC-AWAIT. Je vous reconflète fortement, vous recherchez également Internet pour plus d'autres exemples comment utiliser cela. Si vous utilisez les dernières créatures .NET, c'est vraiment une bonne idée d'apprendre à utiliser cela. Il est beaucoup plus facile que d'utiliser directement les continuations de la tâche.


Si vous utilisez les dernières créatures .NET, il est vraiment une bonne idée d'apprendre à utiliser ce si correct. Je pense que je dois passer un peu de temps sur cet async attendre la chose. Je vérifierai votre code tout de suite, mais cela devrait fonctionner sans aucun problème.


@Misiu N'oubliez pas de marquer l'une des réponses comme correctes.



3
votes

Si vous utilisez .NET 4.5, vous pouvez utiliser la tâche .Quand code> pour réaliser facilement ce que vous voulez:

public async Task<int> GetFirstCompletedTaskAsync()
{
    var tasks = new List<Task> 
    {
        Task.Run(() =>
        {
            Thread.Sleep(5000);
            return 1;
        }),
        Task.Run(() =>
        {
            Thread.Sleep(2000);
            throw new Exception("My error");
        }),
        Task.Run(() =>
        {
            Thread.Sleep(4000);
            return 3;
        }),
    };

    while (tasks.Count > 0)
    {
        var finishedTask = await Task.WhenAny(tasks);
        if (finishedTask.Status == TaskStatus.RanToCompletion)
        {
            return finishedTask
        }

        tasks.Remove(finishedTask);
    }
    throw new WhateverException("No completed tasks");
}


7 commentaires

J'ai besoin d'une version légèrement différente - je dois obtenir d'un premier résultat de toutes les tâches, mais seulement de cette tâche terminée. Si tous sont défaillants, je veux lancer une exception.


Celui-ci a également du temps d'exécution quadratique dans le nombre de tâches. Aussi la consommation de mémoire quadratique pour la continuation.


@usr explique pourquoi cela a une consommation de mémoire et de mémoire quadriques?


@Yuvalitzchakov Il a essayé de blâmer cela sur ma réponse aussi. ;-)


Quand accroche des continuations sur toutes les tâches N et la boucle fonctionne n fois. Dans l'autre réponse, il y avait n Supprimer des opérations chacune d'entre elles prenant des opérations N / 2.


Y a-t-il un moyen d'adopter ce code pour ASP.NET MVC? Il donne une exception aléatoire [InvalidOperationException: un module asynchrone ou un gestionnaire complété lorsqu'une opération asynchrone était toujours en attente.] lorsque vous essayez de l'exécuter pendant que certaines tâches précédentes ne sont pas terminées.


@Antonpapin Le problème avec MVC est que vous devez attendre que toutes les tâches achètent avant le contrôleur terminant la session et renvoyer la réponse. Si vous voulez ignorer cela (que je ne vous recommande pas de vous recommander), vous pouvez utiliser task.factory.startnetw au lieu de la tâche .run qui n'a pas enregistré les continuations avec le temps d'exécution ASP.NET. Pour plus de choses sur ce point de vue Stackoverflow.com/questions/28805796/...



2
votes

Et si tout simplement le faire (au moins cela a fonctionné pour moi): xxx


1 commentaires

C'est la meilleure approche. Au lieu d'écrire à la console, utilisez un TaskCompletSource pour générer une tâche pouvant être attendue.