|
|
Cuarto repaso de 7 del temario para el examen 70-536 de la certificación MCTS en .NET. A quien le guste la programación concurrente como a mi, le gustará el tema 7. En breve escribiré un par de artículos sobre sincronización avanzada de tareas concurrentes, con unos ejemplos de buenas prácticas y de como se diseñan clases thread-safe. |
Creando threads:
· Subprocesamiento administrado.
· Para realizar tareas de forma concurrente, usa la clase Thread.
· Para iniciar la ejecución de un thread, usa el método Thread.Start.
· La enumeración ThreadState representa los distintos estados en los que se puede encontrar un thread.
· La enumeración ThreadPriority representa las prioridades de ejecución que puede tener un thread.
· Utiliza el delegado ThreadStart para iniciar threads sin parámetros.
· Utiliza el delegado ParameterizedThreadStart para iniciar threads con parámetros.
· Para esperar a la finalización de un thread, usa el método Thread.Join.
· Para cancelar la ejecución de un thread, usa el método Thread.Abort, esto provoca que una excepción ThreadAbortException salte en el thread, abortando su ejecución.
· Las regiones críticas (marcadas entre Thread.BeginCriticalRegion y Thread.EndCriticalRegion) son zonas de código que han de ejecutarse como una operación atómica, sin que se pueda abortar la ejecución del thread en el transcurso de esa zona.
· Para compartir datos entre threads, usa la clase ExecutionContext.
· Para suprimir el paso de datos de contexto a los threads puedes usar ExecutionContext.SupressFlow y restaurarlo con ExecutionContext.RestoreFlow.
Compartiendo datos entre threads.
· Para realizar operaciones atómicas, usa la clase Interlock.
· Que una operación esta formada por dos suboperaciones Interlock consecutivas no asegura la atomicidad de la operación global en si.
· Para bloquear datos y conjuntos de instrucciones, usa la instrucción lock.
· lock es una forma de usar la clase Monitor, cuyo objetivo es el mismo.
· Los métodos Monitor.Enter y Monitor.Exit vienen a cumplir el cometido de lock, pero es importante asegurarse de que todo “Enter” tiene su correspondiente “Exit”, por ejemplo mediante un try-finally.
· Al marcar una variable como volatile se indica que dicha variable será accedida por varios hilos y no debe ser cacheada ni aplicarsele ninguna optimización del compilador. Pero esto no asegura que no se provoquen inconsistencias en todas las casuísticas, asi que es mejor usar técnicas como Monitor, lock e Interlock. Hablaré de esto en un artículo en breve, porque es además de interesante, causa de muchos malentendidos.
· Para bloquear datos a un solo “escritor” a la vez pero que pueda ser accedido por muchos “lectores” usa la clase ReaderWriterLock.
· Bloqueos de lector y escritor.
· Los métodos ReaderWriterLock.AcquireReaderLock, ReaderWriterLock.ReleaseReaderLock, ReaderWriterLock.AcquireWriterLock y ReaderWriterLock.ReleaseWriterLock controlan el acceso al recurso.
· Si no se consigue hacer un “acquire” en un tiempo especificado salta una excepción ApplicationException que es necesario manejar.
· Un acceso tanto en modo “reader” como “reader” puede ser actualizado a su antónimo y viceversa con los métodos DowngradeFromWriterLock y UpgradeFromWriterLock.
· Para sincronizar entre threas a través de AppDomains ó procesos limítrofes, usa un Mutex. Es un objeto kernel no administrado y es mucho más pesado que Monitor (unas 33 veces).
· Para regular el acceso a un objeto, usa un Semaphore. Es como un array de Mutex. Tambén es un objeto kernel no administrado, también puede usarse entre AppDomains y procesos limítrofes, y es aún más pesado que Mutex.
· Para señalizar threads a través de AppDomains ó procesos limítrofes, usa Event.
· Un deadlock es un caso donde dos threads quedan bloqueados esperando a recursos que nunca se liberarán.
Modelo de programación Asíncrono:
· El modelo de desarrollo asíncrono (APM) puede mejorar la experiencia de usuario permitiendo multiples operaciones en paralelo al mismo tiempo (concurrentes), mejorando la respuesta de la aplicación.
· Ejemplos del modelo asíncrono.
· En el modelo asíncrono, se crea un delegado que apunte a la función que se quiere ejecutar en segundo plano y se llama a su método .BeginInvoke.
· Hay tres modelos de ejecución:
o Wait-Until-Done: Se trata de hacer lo que en futbol se llama “una pared”, es decir, se lanza la función en segundo plano, se ejecutan otras tareas y después se espera a que la tarea en segundo plano anterior acabe para proseguir.
o Polling: Consiste en lanzar la tarea asíncrona sin importarnos si finaliza, como lo hace ó cuando.
o CallBack: El recomendado. Se trata de indicar un delegado de tipo AsyncCallBack que se ejecutará cuando la tarea asíncrona haya finalizado.
· Para realizar una operación asíncrona sin la carga que produce el uso de la clase Thread, APM usa la clase ThreadPool.
· El número de threads en un ThreadPool esta limitado, pero puede alterarse a través de sus métodos estáticos.
· Para crear operaciones periódicas disparadas por un temporizador, usa la clase Timer. Hay tres tipos:
o System.Windows.Forms.Timer: Se ejecuta en el mismo thread, normalmente, en el de la UI.
o System.Threading.Timer: Se ejecuta en un thread distinto.
o System.Timers.Timer: Es un wrapper del anterior para usarlo en tiempo de diseño con Visual Studio 2005.
· Los parámetros principales del Timer de Threading son:
o Callback: Delegado que se ejecutará cuando salte el timer.
o State: Objeto con información de estado que podemos portar a la ejecución del CallBack.
o DueTime: Tiempo de retraso antes de que se ponga en funcionamiento por primera vez o de nuevo.
o Period: Tiempo en espera.
· Una vez en ejecución, para hace efectivo un cambio de configuración hay que llamar al método Timer.Change.
· Para obtener el resultado de una operación asíncrona, usa la interfaz IAsyncResult.
· Estate preparado para “cazar” excepciones cuando completes una operación asíncrona, pues si ocurriese alguna se disparan al llamar al método .EndXXX.
Creando dominios de aplicaciones:
· Un dominio de aplicación es un contenedor lógico que permite a multiples ensamblados ejecutarse en un único proceso pero previene de accedan a la memoria de otros ensamblados. Crea un dominio de aplicación siempre que quieras ejecutar un ensamblado.
· Dominios de aplicación en C#.
· Aislar los ensamblados en ejecución provee confiabilidad, ya que podemos descargar un ensamblado que este comportándose de forma errática, también aumenta la eficiencia ya que podemos descargar un ensamblado simplemente porque ya no lo necesitamos, siendo esto muy útil para aplicaciones de tipo servidor que se ejecutan durante largos periodos de tiempo. Pero no mejora el rendimiento.
· La clase AppDomain contiene métodos para definir privilegios, carpetas y otras propiedades para el nuevo dominio de aplicación, ejecutar un ensamblado y descargarlo.
· Para instanciar un AppDomain, llama al método estático AppDomain.CreateDomain. AppDomain no tiene constructores convencionales.
· Para cargar un ensamblado en un dominio de aplicación, crea una instancia de AppDomain y llama al método estático AppDomain.ExecuteAssembly.
· Para descargar un dominio de aplicación, llama al método estático AppDomain.Unload.
Configurando dominios de aplicación:
· La forma más simple de usar un dominio de aplicación para ejecutar un ensamblado con privilegios limitados es especificar una zona restringida, como por ejemplo la zona Internet como “evidencia”.
· La clase Evidence, representa la “evidencia”. Una evidencia es la información de un ensamblado que se tiene en tiempo de ejecución, incluyendo en que grupos de código se encuentra, y estos determinan los privilegios.
· La enumeración SecurityZone representa las distintas zonas de seguridad que manejan las directivas de seguridad del sistema.
· Para configurar las propiedades de un dominio de aplicación, crea una instancia de la clase AppDomainSetup y úsala para crear el domino de aplicación.
· La propiedad AppDomainSetup.ShadowCopyFiles indica si existe una cache del ensamblado.
· Las propiedades AppDomain.CurrentDomain y Thread.GetDomain obtienen el domino actual de la aplicación en el hilo de ejecución desde el que se llama.
Creando servicios Windows:
· Un servicio Windows es un proceso que se ejecuta en segundo plano, sin interfaz de usuario y en su propia sesión de usuario.
· Arquitectura de programación de servicios Windows.
· Los servicios en .NET implementar la clase abstracta ServiceBase.
· Para crear un servicio Windows, usa Visual Studio para crear un proyecto usando la plantilla de “Windows Service”. Escribe el código para los métodos OnStart y OnStop, y sobrescribe cualquier método que quieras redifini. Añade los instaladores necesarios para tu aplicación tipo servicio. Finalmente, crea un proyecto de instalación e instala el servicio.
· Para implementar un servicio, especifica el nombre, descripción y tipo de inicio. Sobrescribe los métodos OnStart, OnStop, OnPause, OnContinue y OnShutdown si es necesario.
· La enumeración ServiceStartMode representa los distintos modos de inicio de un servicio.
· La enumeración ServiceAccount representa las distintas cuentas integradas que se pueden usar para ejecutar un servicio.
· Para crear un proyecto de instalación para un servicio, primero define las propiedades de un objeto ServiceInstaller para especificar la descripción del servicio, nombre para mostrar, nombre del servicio y el tipo de inicio. Entonces, define las propiedades de un objeto ServiceProcessInstaller para especificar la configuración de la cuenta del servicio. En este punto, puedes instalar manualmente el servicio ó construir un nuevo proyecto de instalación para el servicio.
· Para controlar manualmente un servicio, puedes usar la herramienta de comandos de .NET ó el MMC de Servicios. También puedes hacerlo desde la clase ServiceController.
· La clase ServiceController, también permite monitorizar el estado del servicio.






