2010-08-31 14 views
1

Quelle serait la meilleure approche pour la mise en œuvre des tâches avec une clé qui fonctionne comme suit: -mise en œuvre « seulement l'un des » et « non en parallèle » sémantique avec la bibliothèque parallèle de tâches

Option 1) Une seule de cette La clé est toujours en attente. Peut être utilisé, par exemple, à partir de ASP.NET MVC pour mettre en file d'attente un seul rendu pour une image miniature quel que soit le nombre de fois que l'image est cliquée. Un seul s'exécute, toutes les autres demandes attendent que celui-ci soit terminé.

Option 2) Tous les articles avec la même clé doivent s'exécuter séquentiellement. Peut être utilisé par exemple pour garantir que les opérations qui récupèrent un fichier d'un magasin de sauvegarde dans un cache local n'essaient pas toutes d'obtenir le fichier dans le cache en même temps. L'option 1 est un cas particulier dans lequel les actions suivantes avec la même clé sont simplement rejetées (en général, il suffit d'enregistrer une vérification de fichier existant).

J'ai une WorkQueue existante qui gère ces deux cas (ainsi que l'état de l'appartement, les paramètres ThreadPriority et les degrés maximum de parallélisme). TPL semble être la meilleure solution pour remplacer ceci et apportera des options d'annulation améliorées.

Les tâches imbriquées avec suites semblent prometteuses, mais la gestion d'un dictionnaire de tâches en file d'attente est vite compliquée entre les classes TaskFactory et TaskScheduler. Hériter de la tâche est également problématique car ni TaskFactory ni TaskScheduler ne sont génériques dans Task.

La plupart des exemples de tâches parallèles supposent que l'ensemble des tâches est connu à l'avance. Dans ce cas, de nouvelles tâches sont ajoutées en permanence et doivent être rejetées ou enchaînées sur des tâches existantes en fonction de l'opération demandée et de la clé transmise.

Est-ce que quelqu'un a implémenté quelque chose de similaire à cela en utilisant TPL? approche avez-vous pris dans vos classes Task, TaskScheduler et TaskFactory?

+0

Mauvaise lecture de votre question, oops –

Répondre

1

Peut-être, d'une manière que je peux penser est

  1. Créer une classe wrapper - dire KeyProcessor à la file d'attente des objets pour une clé.
  2. La méthode KeyProcessor.Run() sera capable de toutes les sémantiques de mise en file d'attente dont vous avez besoin. Essentiellement, il cherchera une file d'attente interne pour tout travail en attente et continuera à le faire de façon séquentielle.
  3. Conserver le dictionnaire des objets KeyProcessor.
  4. Pour toute nouvelle tâche, recherchez dans le dictionnaire la même clé. S'il n'existe pas, ajoutez-le. Mettre la tâche en file d'attente. Si ce n'est pas en cours d'exécution, puis le planifier avec TPL en utilisant la méthode Exécuter en tant qu'action. Utilisez ContinueWith pour planifier la tâche du responsable - par exemple, lorsque la tâche exécutant KeyProcessor.Run est terminée, les tâches de continuation peuvent vérifier si d'autres tâches ont été planifiées pour la même clé (depuis qu'elle est terminée) et la redémarrer OU supprimer du dictionnaire.

Tout ce qui précède aurait été difficile à partir du point de synchronisation de thread pas pour quelques collections intéressantes présentes dans l'espace de noms System.Collections.Concurrent. Cela rendrait la logique ci-dessus beaucoup plus simple.Par exemple, ConcurrentDictionary.GetOrAdd permettra de rechercher et/ou ajouter l'objet KeyProcessor de manière thread-safe.

+0

Une fabrique de tâches personnalisée à l'aide de Continuations a pu résoudre ce problème. Article de blog ici: http://blog.abodit.com/2010/09/constrained-parallelism-for-the-task-parallel-library/. Je vais vous donner le score de réponse puisque votre suggestion est proche. –

1

Ce problème est similaire à celui que j'ai résolu en ReactiveXaml, même si le mien a également mémorisé des demandes précédentes. Jetez un oeil au code QueuedAsyncMRUCache (et son blog entry) - ce code combine le TPL avec les extensions réactives pour obtenir ce genre de chose, mais il fait l'importante garantie que la deuxième demande pour la même clé se bloquera sur le premier demande en vol au lieu d'en émettre une autre.

+1

Merci, intéressant, mais pas tout à fait ce dont j'ai besoin. Je devrais ajouter que je veux être capable de tirer-et-oublier aussi bien que le feu-et-attendre sur n'importe quelle tâche qui est ajoutée. Je veux aussi que les clés soient enlevées dès que la dernière a été exécutée. par exemple. mise à jour de l'heure de la dernière visite pour un utilisateur sur un site Web: cinq accès simultanés sont réduits à une écriture, mais un accès ultérieur est enregistré. –

+0

Peut-être au moins vous pouvez obtenir une certaine inspiration de la façon dont je l'ai mis en œuvre –

+0

Assez curieusement, j'ai fini par mettre en œuvre principalement ce que vous avez spécifié pour l'option # 2 pour un projet: https://gist.github.com/2877c8181d5ae4c8ac02 –