2009-05-06 6 views
7

J'utilise le modèle Model-View-Presenter dans un projet WinForms et un de mes problèmes (parmi beaucoup d'autres) est lorsque le formulaire indique au présentateur de faire quelque chose, puis non-réactif pendant que le présentateur va de le faire. Heureusement, dans mon projet, je n'ai aucun problème à faire asynchrone tous les appels de présentateur la question est de savoir exactement comment le faire?Meilleures pratiques pour les appels asynchrones dans MVP avec WinForms

Si chaque appel du présentateur vient envelopper dans une nouvelle création de fil? *

new Thread(()=>_presenter.DoSomething()).Start(); 

Quelles sont les meilleures pratiques ici? Que faire si l'utilisateur appuie sur le bouton "Abandonner ce que vous faites"? Comment puis-je avorter avec élégance?

. * De façon réaliste, j'utiliser probablement une sorte de proxy sur le présentateur de le faire plutôt que de mettre la création de fil dans le WinForm

+0

Surpris de ne voir aucune réelle participation ici. J'aurais été intéressé par ça aussi. – Houman

Répondre

2

Je peux seulement prétendre que j'ai réfléchi à cela (avant de lire votre question;). D'abord, je truquerais les endroits où cela compte vraiment; par exemple le chokepoint d'accès à la base de données. S'il y a une place qui ne devrait pas être exécutée dans le contexte "UI" (vous pouvez l'enregistrer de http://msdn.microsoft.com/en-us/library/system.threading.synchronizationcontext.current.aspx dans le thread UI, puis comparer plus tard au contexte de synchronisation non-UI) puis Debug.BitchAndMoan() à ce sujet. Tout calcul plus long (qui «devrait» être clairement séparé dans ses propres variétés, à droite) devrait l'affirmer.

Je suppose que vous devriez au moins rendre le type d'exécution de la fonction de présentateur configurable via l'attribut qui est ensuite obéi par proxy. (juste au cas où vous voulez quelque chose fait en série).

L'annulation d'une tâche est en fait le problème du présentateur, mais vous devez avoir un objet de référence qui indique ce que vous voulez arrêter. Si vous optez pour le proxy, vous pouvez récupérer les threads créés dans la liste des tâches avec IAsyncResult, mais c'est toujours un problème de décider lequel est supposé être annulé si la même action peut être appelée plusieurs fois en parallèle. Vous devez donc fournir à la tâche un nom spécifique à l'appel approprié lorsque vous le lancez; ce qui implique beaucoup trop de logique dans le côté View -> Presenter devrait probablement demander à View de demander à l'utilisateur quelle tâche doit être éliminée. Mon expérience est que cela est généralement fait juste en utilisant des événements (style SCSF). Si je le faisais à partir de zéro, j'irais par procuration puisque SCSF a été une source de souffrance de tellement de façons que je doute de la santé mentale de ses concepteurs.

+0

Pas trop de problèmes avec les chokepoints dans mon application, seulement 5 ou 6 endroits différents où une interaction de l'utilisateur est acheminée vers un présentateur et ils peuvent tous se permettre d'être asynchrone. Que diriez-vous de faire tourner le nouveau fil? nouveau fil? BackgroundWorker? Autre chose? –

+1

I _think_ BW signifie plus pour une tâche connue qui fait toujours la même chose, donc le thread de création devrait être idiomatique .NET ici. En fonction de ce que vous faites, vous pouvez utiliser le pool de threads: http://msdn.microsoft.com/en-us/library/ms973903.aspx. Par défaut, le pool de threads est profond de 25, vous pouvez donc le configurer/tester (cette limite ne s'applique pas si vous lancez simplement un nouveau thread) –

0

Pourquoi ne pas faire le modèle de proxy que vous utilisez accepter quelques rappels retourner les résultats ou annuler?

+0

Je suis désolé, mais cela ne répond en aucun cas à ma question. Je demande quelles sont les meilleures pratiques pour exécuter les appels de manière asynchrone? Nouveau fil? BackgroundWorker? Comment faire spécifiquement un abandon? Je n'ai pas besoin de m'inquiéter des rappels, c'est MVP traditionnel donc le présentateur ne retourne jamais rien. –

4

je place généralement toute action qui peut (réaliste) prendre plus d'une seconde ou deux dans une tâche distincte, quelque chose comme:

public interface ITask 
{ 
    void ExecuteTask (ITaskExecutionContext context); 
    void AfterSuccess(ITaskExecutionContext context); 
    void AfterFailure(ITaskExecutionContext context); 
    void AfterAbortion(ITaskExecutionContext context); 
} 

J'ai aussi une abstraction pour l'exécution de ces tâches:

public interface ITaskExecutor : IDisposable 
{ 
    void BeginTask(ITask task); 
    void TellTaskToStop(); 
} 

l'une des implémentations de cette ITaskExecutor utilise la bonne BackgroundWorker:

public class BackgroundTaskExecutor : ITaskExecutor 
{ 
    public void BeginTask(ITask task) 
    { 
     this.task = task; 
     worker = new BackgroundWorker(); 
     worker.DoWork += WorkerDoWork; 
     worker.RunWorkerCompleted += WorkerRunWorkerCompleted; 
     worker.WorkerSupportsCancellation = true; 

     worker.RunWorkerAsync(); 
    } 

    ... 
} 

Je compte beaucoup sur l'injection de dépendance et IoC pour câbler les choses ensemble. Dans le présentateur alors je viens d'appeler quelque chose comme:

GoAndDontReturnUntilYouBringMeALotOfMoneyTask task = new GoAndDontReturnUntilYouBringMeALotOfMoneyTask(parameters); 
taskExecutor.BeginTask(task); 

Annuler/avortent boutons sont ensuite câblés afin qu'ils disent l'exécuteur des tâches/tâche d'avorter.C'est en fait un peu plus complexe que présenté ici, mais c'est l'idée générale.

+0

Je passerais lambdas pour Execute, AfterSuccess, AfterFailure, AfterAbortion mais à part ça, bon conseil. –

+0

Oui, vous pouvez le faire avec lambdas, mais cela ne serait utile que pour des tâches simples. Une logique de tâche plus complexe mérite sa propre classe. De plus, vous n'avez pas vraiment besoin de mettre en œuvre toutes les méthodes "Après", il serait donc préférable d'implémenter une classe abstraite de base avec des méthodes virtuelles que vous pouvez remplacer si nécessaire. –

Questions connexes