2009-05-06 13 views
7

Estoy usando el patrón Model-View-Presenter en un proyecto de WinForms y un problema (entre muchos) que tengo es cuando el formulario le dice al presentador que haga algo y luego no reactivo mientras el presentador va a hacerlo. Afortunadamente en mi proyecto no tengo ningún problema con hacer asincrónicas todas las llamadas del presentador, la pregunta es ¿cómo hacerlo exactamente?Mejores prácticas para llamadas asincrónicas en MVP con WinForms

caso de cada llamada presentador simplemente ser envuelto en una nueva creación del hilo? *

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

¿Cuáles son las mejores prácticas aquí? ¿Qué pasa si el usuario presiona un botón "Cancelar lo que estás haciendo"? ¿Cómo aborto con gracia?

. * Siendo realistas que probablemente sólo tiene que utilizar algún tipo de proxy en el presentador de hacer esto en lugar de poner la creación de hilos en el WinForm

+0

Sorprendido de no ver la participación real aquí. Me hubiera interesado también en esto. – Houman

Respuesta

2

Solo puedo afirmar que he pensado en esto (antes de leer su pregunta;). Primero, prepararía los lugares donde esto realmente importa; por ejemplo, el punto de estrangulamiento de acceso DB. Si hay un lugar que no se debe ejecutar en el contexto "UI" (puede guardarlo desde http://msdn.microsoft.com/en-us/library/system.threading.synchronizationcontext.current.aspx en el hilo de la interfaz de usuario y luego compararlo más adelante con el contexto de sincronización que no es UI) entonces Debug.BitchAndMoan() al respecto. Cualquier cálculo más largo (que "debería" estar claramente separado en sus propias variedades, correcto;) debería afirmar eso.

Supongo que al menos debe hacer que el tipo de ejecución de la función del presentador se pueda configurar mediante un atributo que luego se obedece mediante un proxy. (en caso de que quieras algo hecho en serie).

Cancelar una tarea es realmente un problema del presentador, pero debe tener un objeto de referencia que diga lo que quiere detener. Si utiliza el proxy, puede elegir los hilos creados en la lista de tareas con IAsyncResult, pero sigue siendo un problema decidir cuál se debe cancelar si se permite que la misma acción se invoque varias veces en paralelo. Por lo tanto, debe proporcionarle a la tarea un nombre de llamada específico cuando lo inicie; lo que implica demasiada lógica en el lado Vista -> El presentador probablemente debería preguntar a Ver para preguntar al usuario cuál de las tareas debería eliminarse.

Mi experiencia es que esto generalmente se soluciona mediante el uso de eventos (estilo SCSF). Si lo hago desde el principio, tomaría el camino del proxy, ya que SCSF ha sido un dolor en tantas formas que dudo de la cordura de sus diseñadores.

+0

No hay demasiados problemas con los puntos críticos en mi aplicación, solo 5 o 6 lugares diferentes donde la interacción del usuario se dirige a un presentador y todos pueden permitirse ser asincrónicos. ¿Qué tal cómo girar realmente el nuevo hilo? ¿nuevo hilo? ¿Trabajador de fondo? ¿Algo más? –

+1

I _think_ BW es más para una tarea conocida que siempre hace "lo mismo", por lo que el hilo de desove debe ser idiomático .NET aquí. Dependiendo de lo que haga, puede usar threadpool: http://msdn.microsoft.com/en-us/library/ms973903.aspx. Por defecto, threadpool tiene una profundidad de 25, por lo que puede buscar en la configuración/prueba (este límite no se aplica si siempre genera un nuevo hilo) –

0

Por qué no hacer el patrón de proxy que utiliza aceptar un par de rellamadas para devolver los resultados o abortar?

+0

Lo siento, pero esto de ninguna manera responde mi pregunta. Me pregunto cuáles son las mejores prácticas para ejecutar llamadas de forma asincrónica. ¿Nuevo hilo? ¿Trabajador de fondo? ¿Cómo hacer un aborto específicamente? No necesito preocuparme por las devoluciones de llamadas, este es el MVP tradicional por lo que el presentador nunca devuelve nada. –

4

generalmente coloco cualquier acción que pueda (realista) tomar más de un par de segundos en una tarea independiente, algo así como:

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

que también tienen una abstracción para el funcionamiento de este tipo de tareas:

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

Una de las implementaciones de este ITaskExecutor está utilizando el 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(); 
    } 

    ... 
} 

Confío mucho en la inyección de dependencia y el IoC para unir las cosas. En el presentador continuación, acabo de llamar algo así como:

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

Cancelar/abortan botones están conectados a continuación, por lo que le dicen al ejecutor de tareas/trabajo para abortar.

En realidad es un poco más complejo que el presentado aquí, pero esta es la idea general.

+0

Pasaría lambdas para Execute, AfterSuccess, AfterFailure, AfterAbortion pero aparte de eso, buen consejo. –

+0

Sí, puedes hacerlo con lambdas, pero eso sería útil solo para tareas simples. La lógica de tareas más compleja merece su propia clase. Además, realmente no necesita implementar todos los métodos "Después", por lo que sería mejor implementar una clase abstracta base con métodos virtuales que puede anular si es necesario. –