2012-02-02 47 views
9

Sé que hay Thread.Sleep y System.Windows.Forms.Timer y Monitor.Wait en C# y Windows Forms. Parece que no puedo imaginar cómo esperar X segundos y luego hacer otra cosa, sin bloquear el hilo.¿Cómo esperar asíncronamente x segundos y ejecutar algo entonces?

Tengo un formulario con un botón. Al hacer clic en el botón, se iniciará un temporizador y esperará 5 segundos. Después de estos 5 segundos, algún otro control en el formulario es de color verde. Al usar Thread.Sleep, toda la aplicación dejará de responder durante 5 segundos, entonces ¿cómo puedo "hacer algo después de 5 segundos"?

+3

sólo tiene que utilizar 'System.Windows.Forms.Timer'. Establezca el temporizador durante 5 segundos y maneje el evento 'Tick'. Cuando el evento se dispare, hazlo. – Ben

+2

Estamos agregando una nueva característica a C# 5 que hace que este tipo de operación asincrónica sea mucho más fácil; Si está interesado en obtener un adelanto, comience con esta publicación de blog: http://blogs.msdn.com/b/csharpfaq/archive/2011/04/13/async-ctp-sp1-refresh.aspx –

+0

Way para ir a Microsoft! –

Respuesta

23

(transcrito a partir de Ben como comentario)

sólo tiene que utilizar System.Windows.Forms.Timer. Configure el temporizador por 5 segundos y maneje el evento Tick. Cuando el evento se dispare, hazlo.

...y deshabilite el temporizador (IsEnabled = false) antes de hacer su trabajo en oder para suprimir un segundo.

El Tick caso puede ser ejecutadas en otro hilo que no se puede modificar su interfaz gráfica de usuario, se puede tomar esto:

private System.Windows.Forms.Timer myTimer = new System.Windows.Forms.Timer(); 

    private void StartAsyncTimedWork() 
    { 
     myTimer.Interval = 5000; 
     myTimer.Tick += new EventHandler(myTimer_Tick); 
     myTimer.Start(); 
    } 

    private void myTimer_Tick(object sender, EventArgs e) 
    { 
     if (this.InvokeRequired) 
     { 
      /* Not on UI thread, reenter there... */ 
      this.BeginInvoke(new EventHandler(myTimer_Tick), sender, e); 
     } 
     else 
     { 
      lock (myTimer) 
      { 
       /* only work when this is no reentry while we are already working */ 
       if (this.myTimer.Enabled) 
       { 
        this.myTimer.Stop(); 
        this.doMyDelayedWork(); 
        this.myTimer.Start(); /* optionally restart for periodic work */ 
       } 
      } 
     } 
    } 

simplemente para la corrección: con asíncrono/esperan, uno puede retrasar la ejecución de algo muy fácil (una sola toma, sin repetir la invocación):

private async Task delayedWork() 
{ 
    await Task.Delay(5000); 
    this.doMyDelayedWork(); 
} 

//This could be a button click event handler or the like */ 
private void StartAsyncTimedWork() 
{ 
    Task ignoredAwaitableResult = this.delayedWork(); 
} 

Para más información, véase "asíncrono y esperar" en MSDN.

+0

Upvoted, buena explicación completa. – Ben

+1

'.BeginInvoke (myTimer_Tick, remitente, e)' no funciona porque "Argumento tipo grupo de métodos no es asignable a system.delegate –

+1

PD: si el intervalo no es tan largo como 5 segundos, necesitarás un bloqueo para evitar que se ejecute un segundo tic antes de que el primero haya tenido la oportunidad de desactivar el temporizador. – mtijn

2

su aplicación se bloquea porque está invocando los 5 segundos de espera/espera en el subproceso principal de la interfaz de usuario. ponga la acción sleep/wait/whatever en un hilo separado (en realidad System.Windows.Forms.Timer debería hacer eso por usted) y cuando se complete invoque la acción que convierte algún control en verde. recuerde marcar InvokeRequired. he aquí una pequeña muestra (SetText puede ser llamado desde otro hilo, si se trata de la llamada en su lugar se invoca en el hilo principal interfaz de usuario en el cuadro de texto está activado):

private void SetText(string text) 
{ 
// InvokeRequired required compares the thread ID of the 
// calling thread to the thread ID of the creating thread. 
// If these threads are different, it returns true. 
if (this.textBox1.InvokeRequired) 
{  
    SetTextCallback d = new SetTextCallback(SetText); 
    this.Invoke(d, new object[] { text }); 
} 
else 
{ 
    this.textBox1.Text = text; 
} 
} 

Tomé la muestra de here (vale la pena una lectura!).

+0

¿Cómo puedo evitar la "Operación entre hilos no válida: Control 'botón' al que se accede desde un hilo que no sea el hilo en el que se creó". excepción entonces? –

+1

Consulte Control.Invoke – Onkelborg

+1

Invocar es mágico. ¡Estupendo! –

0

Creo que lo que estás buscando es System.Timers ... No creo que tengas que configurar un temporizador CWND para lo que estás intentando hacer: echa un vistazo a http://msdn.microsoft.com/en-us/library/0tcs6ww8(v=VS.90).aspx para ver un ejemplo. Debería hacer el truco.

+0

System.Windows.Forms.Timer, no está usando un hilo. –

8

Puede iniciar una tarea asincrónica que lleva a cabo su acción:

Task.Factory.StartNew(()=> 
{ 
    Thread.Sleep(5000); 
    form.Invoke(new Action(()=>DoSomething())); 
}); 

[EDIT]

Para pasar el intervalo en el que simplemente tiene que almacenar en una variable:

int interval = 5000; 
Task.Factory.StartNew(()=> 
{ 
    Thread.Sleep(interval); 
    form.Invoke(new Action(()=>DoSomething())); 
}); 

[/ EDIT]

+0

Eso funcionaría. aunque creo que me gustaría pasar el intervalo. –

+1

No es una buena idea. Usted secuestra un PoolThread por 5 segundos aquí. Y luego solo usa Invoke() para salir del hilo. –

+0

@TonyHopkinson Extendí el ejemplo para pasar el intervalo. – PVitt

1

Lo está viendo mal. Haga clic en el botón, iniciará un temporizador con un intervalo de x segundos. Cuando terminan, el controlador de eventos ejecuta la tarea.

Entonces, ¿qué no quieres que suceda?

Mientras transcurren los x segundos.?

¿Mientras se está ejecutando la tarea?

Si, por ejemplo, no desea que se haga clic en el botón hasta que se realicen el retraso y la tarea. Inhabilítelo en el controlador de clic de botón y habilítelo al finalizar la tarea.

Si todo lo que quiere es un retraso de cinco segundos antes de la tarea, entonces debe pasar el retraso de inicio a la tarea y dejar que se encargue de ello.

0

@eFloh en el puesto marcado como respuesta dijo:

El evento Tick pueden ser ejecutadas en otro hilo que no se puede modificar su interfaz gráfica de usuario, se puede tomar esto ...

Eso no es lo que dicen los documentos.
Está utilizando un System.Windows.Forms.Timer en su código de ejemplo.
Eso es Forms.Timer.
De acuerdo con los documentos de C#, los eventos de temporizador se generan en el subproceso de la interfaz de usuario.

temporizador Este Windows está diseñado para un entorno de un solo subproceso donde se utilizan hilos de interfaz de usuario para realizar el procesamiento. Se requiere que el código de usuario tiene un suministro de mensajes de interfaz de usuario disponibles y siempre funciona de la misma hilo ...

ver también stackoverflow post here

Cuestiones relacionadas