2011-11-06 7 views
8

Necesito un mejor diseño para la tarea para dormir que Thread.Sleep, estoy usando la clase Task.Tarea de espera por x cantidad de tiempo e informe de progreso

En mi aplicación wpf ejecuto una Tarea, esta tarea ejecuta otras dos tareas, cada una de estas tareas primero inicia sesión y el sitio web de Internet, después de iniciar sesión necesitan esperar unos segundos e informar al usuario cuándo procederán más lejos y este es mi problema, porque con Thread.Sleep no puedo informar el progreso.

Existen más funciones que el inicio de sesión, aproximadamente 5-6, todas solicitan un recurso de Internet, y entre ellas debe haber un tiempo de espera con el informe de tiempo restante enviar hacer gui, esta función de 5-6 son todas en una tarea, pero podría haber muchas tareas.

Lo que necesito es hacer que la tarea espere, pero también le permite enviar actualizaciones sobre el tiempo restante para proceder a la GUI.

¿Tiene alguna idea para este problema, cómo mejorarla, tal vez ya tenga algunos patrones de diseño para tal problema?

He oído también que el uso de Thread.Sleep es una práctica de diseño deficiente.

EDITAR: Nadie lo sabe? ¿Qué pasa con algún tipo de temporizador autónomo con espera de hilo como waithandle, autoresetevent, anyone?

+1

que tiene una buena respuesta a esta pregunta hace un tiempo atrás: http://stackoverflow.com/questions/7311468/best-practice-for-thread-delays-with-countdown-notification – drharris

+0

Thx, muy útil . – Programista

Respuesta

1

Olvídate de utilizar Thread.Sleep. En su lugar, ejecute su tarea en un hilo de fondo y use un WaitHandle con un AutoResetEvent. (Enlaces: WaitHandle/WaitOne/AutoReset)

Su subproceso en segundo plano puede enviar actualizaciones de la interfaz de usuario con los delegados asincrónicos, pero el subproceso de llamada esperará hasta que sea lo que sucede:

1) Los informes de hilo que ha completado su tarea, usando yourwaithandle.Set();

o

2) los tiempos de espera de hilo de espera (tiempo de espera se establece como parámetro para WaitOne() -method).

+0

Sí, estaba pensando lo mismo desde el principio, pero nadie apuntaba en esta dirección, por eso no sé también el enlace del drharris es muy útil, así que Haré algunos lo gic solo, basado en waithandle o reset event y timer. – Programista

1

En lugar de tener una sola tarea para realizar todas esas media docena de funciones (inicio de sesión, etc.), haga que cada función sea una tarea separada. A continuación, puede crear una cola de prioridad, ordenada por tiempo. Cuando desee iniciar una de estas meta-tareas, coloque su primera función en una cola junto con los datos de estado (es decir, URL, ID de usuario, contraseña, etc.).

A medida que se completa cada función, pone en cola la siguiente función que se realizará. Así que si las dos primeras funciones se registren y se get_data, tendría que:

queue login with state data to execute at DateTime.Now 
When login is finished, it queues get_data with the state data and DateTime.Now + 5 seconds (or whatever time) 

Cuando la última función se ejecuta, publica los resultados en alguna parte.

Puede configurar un temporizador que sondeará la cola 10 veces por segundo (o la próxima vez que se actualice el temporizador cada vez que se agregue algo a la cola). Puede comenzar tareas individuales según sea necesario.

El nuevo CTP Async puede tener ya este tipo de cosas: una "tarea de ejecución en el momento". Puede valer la pena investigarlo.

En cuanto al informe del progreso, cada función puede informar el progreso cuando se inicia (es decir, "iniciar sesión" ... "esperar 5 segundos para obtener datos" ... "obtener datos", etc.) Si lo desea, puede hacer que el hilo del temporizador recorra la cola de prioridad periódicamente e informe cuándo se ejecutarán determinadas tareas. Aunque eso podría ser excesivo.

Y lo que escuchó es correcto: Thread.Sleep es una idea excepcionalmente mala, especialmente cuando se trabaja con subprocesos de grupo de subprocesos.

+0

Esta no es una buena solución, incluso en una aplicación multiproceso, necesita un código estructural, función 5-6 en cada tarea. La tarea es buena, todas están relacionadas, sería un desastre hacer cada función en una tarea separada, cada tarea es un buen pedazo de código, no creo que la función de cola sea una solución real, estoy buscando algo mejor, no hay nada de malo en que estas funciones estén juntas en una tarea, este código es bueno, pero la parte de dormir no. Me niego a pensar que no hay mejor manera. No puede ser "esperar 5 segundos", debe ser una cuenta regresiva real, y el monitoreo periódico es algo así como "piratear". – Programista

+0

@Programista: El punto es que la única manera de retrasar un hilo es dormir. Y como dormir es malo, tienes que inventar otra forma de romper las cosas. –

+0

Eso no es cierto, la tarea puede ser detenida por otra función pero necesito que alguien que ya lo haya hecho me diga cómo, especialmente la actualización de tiempo restante. – Programista

6

La solución es tratarlo como una devolución de llamada asíncrona, en lugar de una espera síncrona. Si usted está en la asíncrono CTP, de la manera correcta sería:

async item => { await Task.Delay(1000); Process(item); }

Esto también parece ser un caso de uso ideal para DataFlow o Rx.

Uso de extensiones reactivas:

static void Track(int timeout, int frequency, string item) 
    { 
     Observable.Interval(TimeSpan.FromSeconds(frequency)) //produces 0, 1, 2.. with the interval 
        .Do(i => Console.WriteLine("Working on {0}", item)) // work on item 
        .TakeUntil(Observable.Timer(TimeSpan.FromSeconds(timeout))) //stop once the timer publishes a value 
        .Subscribe 
        (
         i => Console.WriteLine("Reporting {0}%", ((double)(i + 1)/timeout * 100)), // the interval reaches OnNext 
         e => Console.WriteLine("Error!"), // error occured 
         () => Console.WriteLine("Completed") // observable completed 
       ); 
    } 

en llamar a esto con Track(timeout: 5, frequency: 1, item: "http://example.com/?"), la salida producida es:

Working on http://example.com/? 
Reporting 20% 
Working on http://example.com/? 
Reporting 40% 
Working on http://example.com/? 
Reporting 60% 
Working on http://example.com/? 
Reporting 80% 
Completed 
+0

Interesante, no puedo usar Async CTP porque es una aplicación comercial lista para producción, pero verificaré esa opción de la manera normal con algunas clases de ayuda. ¿Qué opina de algún evento de reinicio automático o manual en un escenario de bloqueo autónomo con recuento de temporizador interno? – Programista

+0

@Programista El bloqueo no es la solución. Lo que realmente necesitas en este caso es una devolución de llamada. Como usar Continuing Passing Style. Editaré la respuesta para incluir una solución usando las Extensiones Reactivas de Microsoft (Rx) si está interesado. – Asti

+0

No sé Rx, así que eso me ayudaría. – Programista

0

Ha considerado el uso de un temporizador (de System.Timers) para realizar el intersticial " tiempo restante "¿actualizaciones de mensajes? Cada una de sus tareas podría establecer un tiempo esperado para el valor de finalización, luego su tarea de temporizador podría ser responsable de contar y actualizar la interfaz de usuario según sea necesario. No Thread.Sleep required, y Timer ejecutará su código Tick en un hilo del grupo de subprocesos.

+0

Sí, pero solo como una referencia interna en un escenario que algo bloquea el hilo y usa un temporizador como conteo de ticks y luego cuenta los ticks (tick podría ser 100ms, 1 segundo, etc.según el enfoque) y envíe la actualización a ui cada segundo con el tiempo restante, si el tiempo pasa a cero, se libera el hilo y se elimina el hilo de bloque autónomo, el recuento y la actualización de ui. – Programista

+0

Estoy hablando de un temporizador general global que controlaría el progreso de las tareas individuales. Creo que estás tratando de hacer demasiado en las tareas individuales; si el final de una tarea también elimina la IU, eso levanta una bandera roja para mí. –

+0

No, no, no puede funcionar como monitor, la tarea en sí debería estar bloqueada, por ejemplo, durante 3 segundos, pero necesito mostrarla en la interfaz de usuario "wait 3..2..1", y así sucesivamente. Tal vez un temporizador podría usarse como una referencia de tiempo global de 100 ms para fines de cuenta atrás para otros objetos, y el ui no está dispuesto, me refiero al objeto/clase individual con lógica que bloquea el hilo, cuenta el tiempo restante y el ui actualizado está dispuesto cuando el tiempo llega a 0 y se lanza el hilo. – Programista

Cuestiones relacionadas