34 votes

Options asynchrones C # pour le traitement d'une liste

Je suis en train d'essayer de mieux comprendre la Async et le Parallèle options que j'ai dans le C#. Dans les extraits ci-dessous, j'ai inclus les 5 approches que je viens dans la plupart des. Mais je ne suis pas sûr de choisir - ou, mieux encore, quels sont les critères à considérer lors du choix:

Méthode 1: Tâche

(voir http://msdn.microsoft.com/en-us/library/dd321439.aspx)

L'appel de StartNew est fonctionnellement équivalent à la création d'une Tâche à l'aide de l'un de ses constructeurs et puis l'appel de Commencer à planifier pour l'exécution. Cependant, à moins que la création et la planification doivent être séparés, StartNew est l'approche recommandée pour tant de simplicité et de performance.

TaskFactory de StartNew méthode devrait être le mécanisme privilégié pour créer et planifier des tâches de calcul, mais pour les scénarios où la création et de la programmation doivent être séparés, les constructeurs peuvent être utilisés, et le Début de la tâche de la méthode peut alors être utilisé pour planifier la tâche de l'exécution à une date ultérieure.

// using System.Threading.Tasks.Task.Factory
void Do_1()
{
    var _List = GetList();
    _List.ForEach(i => Task.Factory.StartNew(_ => { DoSomething(i); }));
}

Méthode 2: QueueUserWorkItem

(voir http://msdn.microsoft.com/en-us/library/system.threading.threadpool.getmaxthreads.aspx)

Vous pouvez faire la queue comme beaucoup de pool de threads de demandes de la mémoire système. Si il y a plus de demandes que le pool de threads, les demandes supplémentaires restent en file d'attente jusqu'à ce que le pool de threads sont disponibles.

Vous pouvez placer des données requises par la file d'attente de la méthode dans les champs d'instance de la classe dans laquelle la méthode est définie, ou vous pouvez utiliser le QueueUserWorkItem(WaitCallback, Objet) surcharge qui accepte un objet contenant les données nécessaires.

// using System.Threading.ThreadPool
void Do_2()
{
    var _List = GetList();
    var _Action = new WaitCallback((o) => { DoSomething(o); });
    _List.ForEach(x => ThreadPool.QueueUserWorkItem(_Action));
}

Méthode 3: Parallèle.Foreach

(voir: http://msdn.microsoft.com/en-us/library/system.threading.tasks.parallel.foreach.aspx)

La classe Parallèle fournit une bibliothèque de base de données parallèle de remplacement pour les opérations courantes telles que les boucles for, pour chacune des boucles, et l'exécution d'un ensemble d'instructions.

Le corps délégué est appelée une fois pour chaque élément de la source énumérable. Il est fourni avec l'élément courant en tant que paramètre.

// using System.Threading.Tasks.Parallel
void Do_3()
{
    var _List = GetList();
    var _Action = new Action<object>((o) => { DoSomething(o); });
    Parallel.ForEach(_List, _Action);
}

Méthode 4: IAsync.BeginInvoke

(voir: http://msdn.microsoft.com/en-us/library/cc190824.aspx)

BeginInvoke est asynchrone; par conséquent, le contrôle retourne immédiatement à l'objet appelant après, il est appelé.

// using IAsync.BeginInvoke()
void Do_4()
{
    var _List = GetList();
    var _Action = new Action<object>((o) => { DoSomething(o); });
    _List.ForEach(x => _Action.BeginInvoke(x, null, null));
}

Méthode 5: BackgroundWorker

(voir: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx)

De mettre en place une opération en arrière-plan, ajouter un gestionnaire d'événements pour l'événement DoWork. Appelez votre temps de fonctionnement de ce gestionnaire d'événement. Pour démarrer l'opération, appelez RunWorkerAsync. Pour recevoir les notifications de mises à jour du progrès, de la poignée de l'événement ProgressChanged. Pour recevoir une notification lorsque l'opération est terminée, la poignée de la RunWorkerCompleted événement.

// using System.ComponentModel.BackgroundWorker
void Do_5()
{
    var _List = GetList();
    using (BackgroundWorker _Worker = new BackgroundWorker())
    {
        _Worker.DoWork += (s, arg) =>
        {
            arg.Result = arg.Argument;
            DoSomething(arg.Argument);
        };
        _Worker.RunWorkerCompleted += (s, arg) =>
        {
            _List.Remove(arg.Result);
            if (_List.Any())
                _Worker.RunWorkerAsync(_List[0]);
        };
        if (_List.Any())
            _Worker.RunWorkerAsync(_List[0]);
    }
}

Je suppose que le critère évident serait:

  1. Est meilleure que les autres pour la performance?
  2. Est meilleure que les autres pour le traitement des erreurs?
  3. Est mieux que l'autre pour le suivi/feedback?

Mais, comment voulez - vouschoisir? Merci d'avance pour vos idées.

15voto

RandomEngy Points 6937

Allez prendre dans un ordre arbitraire:

BackgroundWorker (#5)
J'aime utiliser le BackgroundWorker quand je fais les choses avec une INTERFACE utilisateur. L'avantage qu'il a est d'avoir de l'avancement et des événements feu sur le thread d'INTERFACE utilisateur qui signifie que vous n'obtenez pas méchant exceptions lorsque vous essayez de modifier les éléments de l'INTERFACE utilisateur. Il a aussi un bon moyen intégré de rapports de progrès. Un inconvénient de ce mode est que si vous avez le blocage des appels (comme les demandes web) dans votre travail, vous aurez un fil assis autour de ne rien faire, tout le travail est réalisé. Ce n'est probablement pas un problème si vous ne pense que vous aurez une poignée d'entre eux cependant.

IAsyncResult/début/Fin (APM, #4)
C'est un vaste et puissant, mais difficile de modèle à utiliser. La gestion des erreurs est gênant, car vous avez besoin de re-capturer des exceptions à la Fin de l'appel, et les exceptions ne sont pas nécessairement faire revenir des morceaux de code qui peut s'en occuper. C'est le danger de la permanence de la pendaison des demandes en ASP.NET ou simplement avoir des erreurs disparaissent mystérieusement dans d'autres applications. Vous devez également être vigilant quant à l' CompletedSynchronously de la propriété. Si vous n'avez pas de suivre et de signaler correctement, le programme peut se bloquer et de fuite des ressources. Le revers de la médaille, c'est que si vous êtes en cours d'exécution à l'intérieur du contexte d'un autre APM, vous devez vous assurer que toutes les méthodes asynchrones vous appelez également le rapport de cette valeur. Cela signifie faire un autre APM appel ou à l'aide d'un Task et un moulage d'un IAsyncResult pour accéder à son CompletedSynchronously de la propriété.

Il y a aussi beaucoup de frais généraux dans les signatures: Vous avez à charge un objet arbitraire de passer à travers, faire votre propre IAsyncResult mise en œuvre si vous écrivez une méthode asynchrone qui prend en charge l'interrogation et d'attendre les poignées (même si vous êtes seulement en utilisant la fonction de rappel). Par ailleurs, vous ne devriez utiliser de rappel ici. Lorsque vous utilisez l'attente de poignée ou de sondage d' IsCompleted, vous perdez un thread alors que l'opération est en cours.

Basé sur des événements du Modèle Asynchrone (EAP)
Ce qui n'était pas sur votre liste, mais je vais citer, par souci d'exhaustivité. C'est un peu plus convivial que de l'APM. Il y a des événements au lieu de rappels et il y a moins de malbouffe en suspens sur les signatures de méthode. Erreur de manipulation est un peu plus facile puisque c'est enregistré et disponible dans le rappel plutôt que de re-jeté. CompletedSynchronously ne fait pas partie de l'API.

Tâches (#1)
Les tâches sont un autre amical API asynchrone. Erreur de manipulation est simple: l'exception est toujours là pour l'inspection sur le rappel, et personne ne se soucie CompletedSynchronously. Vous pouvez faire dépendances et c'est un excellent moyen de gérer l'exécution de plusieurs tâches asynchrones. Vous pouvez même terminer en APM ou EAP (un type que vous avez manqué) de méthodes asynchrones en eux. Une autre bonne chose à propos de l'utilisation des tâches est votre code ne se soucie pas de la façon dont l'opération est mise en œuvre. Il peut bloquer sur un fil ou être totalement asynchrone, mais la consommation de code ne se soucie pas de cela. Vous pouvez également mélanger les APM et EAP facilement des opérations avec des Tâches.

En parallèle.Pour les méthodes (#3)
Ce sont des aides supplémentaires sur le dessus de Tâches. Ils peuvent faire une partie du travail pour créer des tâches pour vous et rendre votre code plus lisible, si votre async tâches sont adaptés pour exécuter dans une boucle.

ThreadPool.QueueUserWorkItem (#2)
C'est un bas niveau de l'utilitaire qui est en fait utilisé par ASP.NET pour toutes les demandes. Il n'a pas intégré de gestion d'erreur, tels que les tâches de sorte que vous avez à les attraper tout ce et le tuyau de retour jusqu'à votre application si vous voulez savoir à ce sujet. Il est adapté pour les CPU-intensive de travail, mais vous ne voulez pas mettre les appels de blocage sur elle, comme un synchrones demande web. C'est parce que tant qu'il fonctionne, c'est à l'aide d'un fil.

async / await Mots-clés
Nouvelle dans .NET 4.5, ces mots-clés vous permettent d'écrire du code asynchrone sans autorisation explicite des rappels. Vous pouvez vous attendent sur un Task et tout le code ci-dessous, attendez que l'opération asynchrone pour terminer, sans consommer un fil.

4voto

SpaceghostAli Points 3732

Votre première, troisième et quatrième exemples d'utiliser le pool de threads implicitement car, par défaut, les Tâches sont planifiées sur le pool de threads et de la TPL extensions utiliser le pool de threads ainsi, l'API cache simplement une partie de la complexité de voir ici et ici. BackgroundWorkers font partie de la ComponentModel espace de noms car ils sont conçus pour une utilisation dans l'INTERFACE utilisateur de scénarios.

2voto

seldon Points 956

Réactif extensions est un autre à venir de la bibliothèque pour la manipulation de la programmation asynchrone, surtout quand il s'agit de la composition des événements asynchrones et des méthodes.

Il n'est pas natif, cependant, il est développé par Ms labs. Il est disponible à la fois .NET 3.5 et .NET 4.0 et est essentiellement une collection de méthodes d'extension sur le .NET 4.0 a introduit IObservable<T> interface.

Il y a beaucoup d'exemples et de tutoriels sur leur site principal, et je vous recommande fortement de vérifier quelques-uns d'entre eux. Le modèle peut sembler un peu étrange au début (au moins pour .NET programmeurs), mais bien en vaut la peine, même si c'est juste de saisir le nouveau concept.

La véritable force de réactif extensions (Rx.NET), c'est quand vous avez besoin pour composer plusieurs asynchrone sources et des événements. Tous les opérateurs sont conçus avec cela à l'esprit et gère le laid parties de l'asynchronie pour vous.

Site principal: http://msdn.microsoft.com/en-us/data/gg577609

Guide du débutant: http://msdn.microsoft.com/en-us/data/gg577611

Exemples: http://rxwiki.wikidot.com/101samples

Cela dit, le meilleur modèle asynchrone dépend probablement de ce que la situation vous vous trouvez. Certaines sont meilleures (plus simple) pour de simples trucs et certains sont plus extensible et plus facile à gérer quand il vient à des scénarios plus complexes. Je ne peux pas parler pour tous ceux que tu mentionnes bien.

-2voto

xumix Points 122

Le dernier est le meilleur pour 2,3 au moins. Il a des méthodes / propriétés intégrées pour cela. Les autres variantes sont presque les mêmes, juste différentes versions / wrappers convinient

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X