with visual threadstart threads thread new net multi method example español book c# multithreading

visual - En C#, ¿espera en el hilo principal mientras continúa procesando las actualizaciones de UI?(.NET 2.0 CF)



threadstart c# (10)

De lo contrario, bloquearé la ejecución del código en el hilo principal y permitiré que se muestren los cambios de UI.

Traté de encontrar una versión de ejemplo simplificada de lo que estoy tratando de hacer; y esto es lo mejor que pude hacer. Obviamente no demuestra el comportamiento que estoy esperando o no estaría publicando la pregunta. Solo espero que me brinde algún contexto de código para respaldar mi pobre explicación del problema que espero resolver.

Dentro de un botón, haga clic en controlador en un formulario Tengo esto:

private void button2_Click(object sender, EventArgs e) { AutoResetEvent autoResetEvent = new AutoResetEvent(false); new Thread(delegate() { // do something that takes a while. Thread.Sleep(1000); // Update UI w/BeginInvoke this.BeginInvoke(new ThreadStart( delegate() { this.Text = "Working... 1"; this.Refresh(); Thread.Sleep(1000); // gimme a chance to see the new text })); // do something else that takes a while. Thread.Sleep(1000); // Update UI w/Invoke this.Invoke(new ThreadStart( delegate() { this.Text = "Working... 2"; this.Refresh(); Thread.Sleep(1000); // gimme a chance to see the new text })); // do something else that takes a while. Thread.Sleep(1000); autoResetEvent.Set(); }).Start(); // I want the UI to update during this 4 seconds, even though I''m // blocking the mainthread if (autoResetEvent.WaitOne(4000, false)) { this.Text = "Event Signalled"; } else { this.Text = "Event Wait Timeout"; } Thread.Sleep(1000); // gimme a chance to see the new text this.Refresh(); }

Si no configuré un timout en WaitOne (), la aplicación quedaría estancada en la llamada a Invoke ().

En cuanto a por qué me gustaría hacer esto, me han encargado trasladar un subsistema de una aplicación para que trabaje en un hilo de fondo, pero todavía tengo que bloquear el flujo de trabajo del usuario (el hilo principal) solo algunas veces y para ciertos tipos de trabajo relacionado solo con ese subsistema.


Desea utilizar la clase " BackgroundWorker ", que le quitará la mayor parte de este dolor. Pero como se mencionó anteriormente, también deberá estructurarlo de modo que el hilo principal esté actualizando la interfaz de usuario y el trabajador esté haciendo el trabajo pesado.


Es más fácil entonces podrías pensar.

Sugerencia: cuando necesite un hilo para realizar algún trabajo ocasional, descárguelo del grupo de subprocesos, de modo que no necesitará código de reciclaje propenso a errores o extraños.

Cuando desee algo en otro hilo para actualizar su UI, solo necesita hacer una referencia al formulario y llamar a Form.Invoke pasando el código de UI que desea que ejecute el hilo principal; es una mejor pactice, en un evento, lanzar el hilo de interfaz de usuario lo antes posible.

Es decir:

private void button1_Click(object sender, EventArgs e) { // this is the UI thread ThreadPool.QueueUserWorkItem(delegate(object state) { // this is the background thread // get the job done Thread.Sleep(5000); int result = 2 + 2; // next call is to the Invoke method of the form this.Invoke(new Action<int>(delegate(int res) { // this is the UI thread // update it! label1.Text = res.ToString(); }), result); }); }

Espero que esto te ayude:)

EDITAR: Lo siento, no leí la parte "bloqueo del flujo de trabajo del usuario".

WindowsForms no está diseñado para hacer eso, bloquear el hilo principal es MALO (maneja los mensajes del sistema operativo).

No tiene que bloquear el flujo de trabajo del usuario mediante la congelación de un formulario (que Windows considerará como "No responde"), la forma de bloquear el flujo de trabajo del usuario es deshabilitando el control que desee (con el método Invoke anterior si de otro hilo), incluso la forma completa !!


Estoy de acuerdo con los otros que sugieren que use Background Worker. Hace el trabajo pesado y permite que la IU continúe. Puede usar el Informe de progreso del trabajador en segundo plano para iniciar las horas en las que se puede configurar el formulario principal para que se desactive mientras realiza las acciones en segundo plano y luego volver a habilitar una vez que las ''ciertas instancias'' hayan completado el procesamiento.

¡Déjeme saber si esto ayuda! JFV


Las actividades comunes que ''bloquean'' el hilo principal son cosas como abrir cuadros de mensajes o diálogo modal. El código principal parece bloquearse en la llamada a MessageBox o ShowDialog.

La forma en que funcionan estos elementos (y MessageBox es solo un diálogo modal especializado) es que contienen su propia bomba de mensajes mientras bloquean.

Aunque es un hack desagradable, puede hacer algo como esto en su aplicación haciendo un bucle llamando a Application.DoEvents () para mantener los mensajes de los usuarios mientras espera que su otra tarea se complete. Debe tener cuidado ya que todo tipo de cosas desagradables pueden provocar el bombeo de mensajes como este, por ejemplo, que alguien cierre el formulario o vuelva a ingresar su actual controlador de mensajes; los diálogos modales lo evitan desactivando efectivamente la entrada desde el formulario que los inicia.

Quise decir que BackgroundWorker es una mejor solución, si puedes hacer que encaje. A veces lo combino con un ''diálogo de progreso'' modal para darme el bombeo de mensajes / hilos de fondo y el bloqueo del hilo de UI.

Editar - para expandir en el último bit:

Un enfoque que he usado es tener una clase de ''forma de progreso'', que toma un objeto BackgroundWorker como un parámetro constructor, y contiene controladores para los eventos de progreso y finalización del trabajador en segundo plano que se le pasa.

El formulario que quiere que se realice el trabajo crea el trabajador de segundo plano y conecta el evento ''trabajo'' (no puede recordar cómo se llama ahora), y luego crea un diálogo de progreso al cual pasa el trabajador de segundo plano. A continuación, muestra de forma modal el cuadro de diálogo de progreso, lo que significa que esperará (pero enviará mensajes) hasta que se cierre el cuadro de diálogo de progreso.

El formulario de progreso es responsable de iniciar BackgroundWorker desde su sobrescritura OnLoad, y se cierra cuando ve el BackgroundWorker completo. Obviamente, puede agregar texto de mensaje, barras de progreso, botones de cancelar, lo que sea al formulario de progreso.


Probablemente deberías reestructurar tu código como otros lo han sugerido, pero dependiendo del comportamiento que estés buscando, también puedes echar un vistazo al uso de Thread.Join en tu hilo de trabajo en segundo plano. Unirse realmente permite que el hilo que llama procese los eventos COM y SendMessage mientras espera que termine el otro hilo. Parece que podría ser peligroso en algunos casos, pero en realidad tuve un par de escenarios en los que era la única forma de esperar a que otro hilo terminara limpiamente.

Thread .. ::. Join Method

Bloquea el hilo que llama hasta que termina un hilo, mientras continúa realizando el bombeo estándar de COM y SendMessage.

(desde http://msdn.microsoft.com/en-us/library/95hbf2ta.aspx )


Si pudieras ajustar tu código para que establezcas un indicador una vez que haya comenzado un proceso y luego verifiques que en la interfaz de usuario antes de comenzar una operación adicional, creo que te resultará mucho más fácil codificar esto. Crearía un delegado al que se podría llamar desde el hilo en el threadpool o hilo creado por el usuario para actualizar el progreso en la UI. Una vez que el proceso en segundo plano se ha completado, cambie la bandera y las operaciones de UI normales pueden continuar. La única advertencia que debe tener en cuenta es que cuando actualice los componentes de la interfaz de usuario, debe hacerlo en el hilo en el que se crearon, el hilo principal / interfaz de usuario. Para lograr esto, puede llamar al método Invoke () en cualquier control que viva en ese hilo y pasarle el delegado y los parámetros que necesita para llamarlo.

Aquí hay un enlace a un tutorial que escribí hace un tiempo sobre cómo usar Control.Invoke ():

http://xsdev.net/tutorials/pop3fetcher/


estructurar su aplicación para que el hilo principal solo realice actualizaciones de UI, y todo el resto del trabajo se realice en hilos secundarios a través de una cola de trabajos; luego agrega una bandera de espera de Godot a tu hilo principal y úsala para proteger el método que agrega elementos a la cola de trabajo

por curiosidad: ¿por qué quieres hacer esto?


Solo un fragmento de código: no tengo mucho tiempo, lo siento :)

private void StartMyDoSomethingThread() { Thread d = new Thread(new ThreadStart(DoSomething)); d.Start(); } private void DoSomething() { Thread.Sleep(1000); ReportBack("I''m still working"); Thread.Sleep(1000); ReportBack("I''m done"); } private void ReportBack(string p) { if (this.InvokeRequired) { this.Invoke(new Action<string>(ReportBack), new object[] { p }); return; } this.Text = p; }


Lo mejor es enviar el trabajo, pero si es necesario, tal vez algo como esto. Simplemente llame a este método para esperar la señal en lugar de llamar a la espera.

private static TimeSpan InfiniteTimeout = TimeSpan.FromMilliseconds(-1); private const Int32 MAX_WAIT = 100; public static bool Wait(WaitHandle handle, TimeSpan timeout) { Int32 expireTicks; bool signaled; Int32 waitTime; bool exitLoop; // guard the inputs if (handle == null) { throw new ArgumentNullException("handle"); } else if ((handle.SafeWaitHandle.IsClosed)) { throw new ArgumentException("closed wait handle", "handle"); } else if ((handle.SafeWaitHandle.IsInvalid)) { throw new ArgumentException("invalid wait handle", "handle"); } else if ((timeout < InfiniteTimeout)) { throw new ArgumentException("invalid timeout <-1", "timeout"); } // wait for the signal expireTicks = (int)Environment.TickCount + timeout.TotalMilliseconds; do { if (timeout.Equals(InfiniteTimeout)) { waitTime = MAX_WAIT; } else { waitTime = (expireTicks - Environment.TickCount); if (waitTime <= 0) { exitLoop = true; waitTime = 0; } else if (waitTime > MAX_WAIT) { waitTime = MAX_WAIT; } } if ((handle.SafeWaitHandle.IsClosed)) { exitLoop = true; } else if (handle.WaitOne(waitTime, false)) { exitLoop = true; signaled = true; } else { if (Application.MessageLoop) { Application.DoEvents(); } else { Thread.Sleep(1); } } } while (!exitLoop); return signaled; }


Fui con algo que aún no había visto publicado y que es usar MessageQueues.

  • The MainThread bloquea mientras espera el siguiente mensaje en una cola.
  • El hilo de fondo muestra diferentes tipos de mensajes en MessageQueue.
  • Algunos de los tipos de mensaje señalan a MainThread para actualizar los elementos de la IU.
  • Por supuesto, hay un mensaje para decirle a MainThread que deje de bloquear y espere mensajes.

Parece exagerado teniendo en cuenta que el bucle de mensajes de Windows ya existe en alguna parte, pero funciona.