Sobre el porqué recomienda la documentación el uso de 1 en vez de 0 en Thread.Sleep(1) para Silverlight

Hola

Iba a poner esto como respuesta al artículo de Octavio sobre “Qué problema tiene este código”, pero lo pongo en artículo aparte por si le sirve a alguien más. La documentación oficial de Silverlight recomienda, en efecto, usar System.Threading.Thread.Sleep(1) (con un 1 en vez de un 0), cuando queremos suspender el subproceso actual y dar prioridad a otros subprocesos en espera de ejecución. La razón principal no la sé al 100%, pero sí que hay un par de cosas importantes en este escenario:

a) por un lado la documentación advierte que “No llame a Sleep
desde el subproceso de la aplicación principal en la aplicación basada
en Silverlight. Si lo hace, la interfaz de usuario dejará de responder,
se bloquearán los eventos BackgroundWorker y DispatcherTimer, y se bloquearán las llamadas entre subprocesos al subproceso de la interfaz de usuario que se realizaron utilizando
Dispatcher.

b) Pero, sobre todo, hay que recordar que una aplicación Silverlight actualiza la interfaz de usuario desde la hebra principal de ejecución. Esto puede provocar situaciones intolerables cuando se están procesando grupos de ficheros locales, por ejemplo, y la IU no hace nada hasta que ese proceso concluye (pongamos por caso, creando bitmaps a partir de ficheros gráficos del equipo del usuario que van a ser presentados en pantalla). En este caso, debiéramos dejar la responsabilidad de la creación del Bitmap a un método que sea llamado cada vez que se produzca el evento CompositionTarget.Rendering, en una instrucción del tipo CompositionTarget.Rendering += new EventHandler(CrearBitmap). Si lo hacemos así, cada vez que un nuevo elemento (Bitmap, aquí), este construido y listo para ser mostrado, el motor de interpretación visual (rendering) actualizará la IU evitando esos lapsus por  falta de respuesta.

c) El uso de System.Threading.Timer en Silverlight debiera de hacerse cuidadosamente, y, mejor aún, pensar en soluciones con DispatcherTimer, como demuestra Emil Stoychev en su artículo “Tip: Asynchronous Silverlight – Execute on the UI thread“.

d) Lo del 1 por el 0 será para darle tiempo a la pobre (hebra). De todas formas, y si os gusta eso del debugging a más bajo nivel -David (Salgado) y Pablo (Alvarez)…por si no la habíais leído…-)), fijaros lo que dice esa maravillosa “monstrua” que es Tess Ferrandez en “Debugging Silverlight applications with windbg and sos.dll

Saludos

Marino

5 comentarios en “Sobre el porqué recomienda la documentación el uso de 1 en vez de 0 en Thread.Sleep(1) para Silverlight”

  1. Conozco a Tess, La llevé de ponente al teched del año pasado 😀 es Genial!
    Muy interesante el post sobre SL internals… de modo que …hasta las tecnologías de UX pueden tener chicha!! O=D

  2. ¡Excelente, Marino!

    Cierto que esto merecía un post independiente. Me quitaste la idea, pero por ser tú, te lo perdono :-).

    Abrazo – Octavio

  3. La diferencia entre 0 y 1 está documentada en el Sleep de Win32: básicamente pasando un cero duermes el hilo hasta que se vacíe la mayor parte del bucle de mensajes y todos los demás hilos hayan tenido su cuota antes de volver al que está dormido (es una especie de “despiertame cuando puedas, sin prisas”).

    Pasar cualquier otro valor significa que sólo quieres dormir ese tiempo en milisegundos y que debes despertar transcurrido dicho tiempo. Pasarle un 1 significa que quieres dormir lo mínimo posible. Eso la teoría, la práctica dice que, como poco, vas a tener una suspensión de al menos 15 ms (el time-slice mínimo de windows), aunque dependiendo de la carga del proceso actual, y del ordenador en sí, esos tiempos pueden llegar a ser del orden de los cientos de milisegundos. También entra en juego la prioridad del hilo en relación a los demás, e incluso el número de “cores” del ordenador…

    Así que, suponiendo que el diseño sea similar en .NET (y por mi experiencia así lo parece), la diferencia sería la misma: en el primer caso me despiertas cuando quieras, en el segundo lo haces en cuanto puedas.

  4. Muy oportuno, Rafael. Y para trabajos en paralelo se recomienda BackgroundWorker, ya que hay métodos de actualizar la IU desde otro hilo y evitar a toda costa Sleep en la hebra principal (por que la duerme, como tú dices).

    Lo que nos lleva a otro error muy común que no he comentado antes: muchos piensan que las llamadas asíncronas (acceso a servicios, p.e) se ejecutan en otro hilo distinto. No hay tal. Se encolan en el mismo hilo de ejecución esperando su proceso. Por eso podemos asignar los resultados directamente a la IU sin tener que llamar a Invoke.

    Y hay también algunas otras cosas que funcionan distinto debido a que el CLR de Silverlight está hecho con PAL (Platform Adaptation Layer) y ahí se han eliminado un montón de opciones que tiene el CLR estándar porque no hacían falta/se requería minimizar el peso del “runtime”.

    Me gustaría que la gente valorase eso. Es casi un milagro meter lo que han metido en 4,3 Mb, si comparáis con el tamaño del CLR normal.

    Saludos
    Marino

  5. Excelente exposicion Marino.

    Unicamente apuntar o mejor dicho resaltar lo que dices en el punto b. No soy un experto en Silverlight pero he sufrido en mis propias carnes las restricciones de modificación de controles UIElement fuera del hilo principal, lo qual no está permitido. Caí en el error que mediante BackgroundWorker podrìa tener acceso a los controles de UI y no es así. Aqui quien manda es la hebra principal.

    En definitiva, no utilizar en el evento DoWork del BackgroundWorker para operaciones de modificacion de la UI.

    Saludos!!!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *