typeparam example cref code c# .net winapi events execution

example - La mejor práctica para una ejecución de código sin fin/periódica en C#



summary example c# (7)

A menudo, en mi Código, comienzo amenazas que básicamente se ven así:

void WatchForSomething() { while(true) { if(SomeCondition) { //Raise Event to handle Condition OnSomeCondition(); } Sleep(100); } }

solo para saber si alguna condición es verdadera o no (por ejemplo, si tengo una biblioteca mal codificada sin eventos, solo variables booleanas y necesito una "vista en vivo" de ellas).

Ahora me pregunto si hay una forma mejor de realizar este tipo de trabajo, como una función de Windows para enganchar, que puede ejecutar mis métodos todo x seg. ¿O debería codificar un evento global para mi aplicación, aumentar todos los segundos y dejar que llame a mis métodos de esta manera?

//Event from Windows or selfmade TicEvent += new TicEventHandler(WatchForSomething));

y luego este método:

void WatchForSomething() { if(SomeCondition) { //Raise Event to handle Condition OnSomeCondition(); } }

Entonces, espero que esto no se cierre por ser una "pregunta subjetiva" o algo así, solo quiero saber cuál es la mejor práctica para este tipo de trabajo.


El primer ejemplo que muestra es una forma bastante poco elegante de implementar un temporizador periódico. .NET tiene una serie de objetos de temporizador que hacen que este tipo de cosas sea casi trivial. Consulte System.Windows.Forms.Timer , System.Timers.Timer y System.Threading.Timer .

Por ejemplo, así es como usaría un System.Threading.Timer para reemplazar su primer ejemplo:

System.Threading.Timer MyTimer = new System.Threading.Timer(CheckCondition, null, 100, 100); void CheckCondition(object state) { if (SomeCondition()) { OnSomeCondition(); } }

Ese código llamará a CheckCondition cada 100 milisegundos (más o menos).


No proporciona muchos antecedentes sobre por qué está haciendo esto o lo que está tratando de lograr, pero si es posible, es posible que desee considerar la creación de un servicio de Windows.


Por cierto, Thread.Sleep probablemente nunca sea una buena idea.

Un problema básico con Thread.Sleep que las personas generalmente no conocen, es que la implementación interna de Thread.Sleep no bombea mensajes STA . La mejor y más fácil alternativa, si tiene que esperar un momento determinado y no puede usar un objeto de sincronización del núcleo, es reemplazar Thread.Sleep con Thread.Join en el hilo actual, con el tiempo de espera deseado. Thread.Join se comportará igual, es decir, el hilo esperará el tiempo deseado, pero mientras tanto se bombearán los objetos STA.

¿Por qué esto es importante (a continuación se detalla una explicación detallada)?

A veces, sin que lo sepas, uno de tus hilos puede haber creado un objeto STA COM. (Por ejemplo, esto sucede a veces detrás de escena cuando usas las API de Shell). Ahora supongamos que un hilo suyo ha creado un objeto STA COM, y ahora está en una llamada a Thread.Sleep . Si en algún momento el objeto COM debe eliminarse (lo que puede suceder en un momento inesperado por parte del GC), el hilo del Finalizer intentará llamar al distructor del objeto. Esta llamada se organizará en el hilo STA del objeto, que será bloqueado.

Ahora, de hecho, tendrá un hilo finalizador bloqueado. En esta situación, los objetos no pueden liberarse de la memoria, y seguirán cosas malas.

Entonces, la conclusión: Thread.Sleep = bad. Thread.Join = alternativa razonable.


Si echa un vistazo a las extensiones reactivas , proporciona una forma elegante de hacerlo utilizando el patrón observable .

var timer = Observable.Interval(Timespan.FromMilliseconds(100)); timer.Subscribe(tick => OnSomeCondition());

Lo bueno de observables es la capacidad de componer y combinar otros observables de los existentes, e incluso utilizar expresiones LINQ para crear otros nuevos. Por ejemplo, si quisieras tener un segundo temporizador sincronizado con el primero, pero solo disparando cada 1 segundo, podrías decir

var seconds = from tick in timer where tick % 10 == 0 select tick; seconds.Subscribe(tick => OnSomeOtherCondition());


Una forma muy simple de no bloquear esperar otros hilos / tareas es:

(new ManualResetEvent(false)).WaitOne(500); //Waits 500ms


Use un BackgroundWoker para medidas de seguridad de subprocesos adicionales:

BackgroundWorker bw = new BackgroundWorker(); bw.WorkerSupportsCancellation = true; bw.WorkerReportsProgress = true; . . . private void bw_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker worker = sender as BackgroundWorker; for (;;) { if (worker.CancellationPending == true) { e.Cancel = true; break; } else { // Perform a time consuming operation and report progress. System.Threading.Thread.Sleep(100); } } }

Para obtener más información, visite: http://msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx


No necesariamente hay una "mejor manera" de escribir código de procesamiento de eventos de larga duración. Depende del tipo de aplicación que esté desarrollando.

El primer ejemplo que muestra es la manera idiomática en la que a menudo verá el método principal de un hilo de larga duración escrito. Aunque generalmente es deseable usar una primitiva de sincronización mutex o de evento waitable en lugar de una llamada a Sleep() ; de lo contrario, es un patrón típico utilizado para implementar bucles de procesamiento de eventos. El beneficio de este enfoque es que permite que el procesamiento especializado se ejecute en un hilo separado, lo que permite que el hilo principal de su aplicación realice otras tareas o permanezca receptivo a la entrada del usuario. La desventaja de este enfoque es que puede requerir el uso de barreras de memoria (como bloqueos) para garantizar que los recursos compartidos no estén dañados. También hace que sea más difícil actualizar su UI, ya que generalmente debe ordenar dichas llamadas al hilo de UI.

El segundo enfoque también se usa a menudo, especialmente en sistemas que ya tienen una API de unidad de eventos como WinForms, WPF o Silverlight. El uso de un objeto de temporizador o evento inactivo es la manera típica en la que se pueden realizar comprobaciones de fondo periódicas si no hay un evento iniciado por el usuario que desencadene su procesamiento. El beneficio aquí es que es fácil interactuar y actualizar los objetos de la interfaz de usuario (ya que son directamente accesibles desde el mismo subproceso) y se mitiga la necesidad de bloquear y excluir a los datos protegidos. Una posible desventaja de este enfoque es que si el procesamiento que debe realizarse consume mucho tiempo, puede hacer que su aplicación no responda a la entrada del usuario.

Si no está escribiendo aplicaciones que tienen una interfaz de usuario (como servicios), entonces la primera forma se usa con mucha más frecuencia.

Además, cuando sea posible, es mejor usar un objeto de sincronización como EventWaitHandle o Semaphore para indicar cuándo el trabajo está disponible para ser procesado. Esto le permite evitar el uso de objetos Thread.Sleep y / o Timer. Reduce la latencia promedio entre cuando el trabajo está disponible y cuando se desencadena el código de procesamiento de eventos, y minimiza la sobrecarga de usar subprocesos de fondo, ya que el entorno de tiempo de ejecución puede programarlos más eficientemente y no consumirá ningún ciclo de CPU. hasta que haya trabajo por hacer.

También vale la pena mencionar que si el procesamiento que realiza es en respuesta a las comunicaciones con fuentes externas (MessageQueues, HTTP, TCP, etc.) puede usar tecnologías como WCF para proporcionar el esqueleto de su código de manejo de eventos. WCF proporciona clases base que hacen que sea sustancialmente más fácil implementar los sistemas cliente y servidor que responden asincrónicamente a la actividad de eventos de comunicación.