2012-02-02 7 views
9

So che c'è Thread.Sleep e System.Windows.Forms.Timer e Monitor.Wait in C# e Windows Form. Semplicemente non riesco a capire come aspettare X secondi e poi fare qualcos'altro - senza bloccare il thread.Come attendere in modo asincrono x secondi ed eseguire qualcosa allora?

Ho un modulo con un pulsante. Sul pulsante clic un timer deve iniziare e attendere 5 secondi. Dopo questi 5 secondi, un altro controllo sul modulo è colorato in verde. Quando si utilizza Thread.Sleep, l'intera applicazione non risponde per 5 secondi, quindi come faccio a "fare qualcosa dopo 5 secondi"?

+3

Basta usare 'System.Windows.Forms.Timer'. Imposta il timer per 5 secondi e gestisci l'evento 'Tick'. Quando l'evento spara, fai la cosa. – Ben

+2

Stiamo aggiungendo una nuova funzionalità al C# 5 che rende molto più semplice questo tipo di operazione asincrona; se sei interessato ad ottenere un picco, inizia con questo post del blog: http://blogs.msdn.com/b/csharpfaq/archive/2011/04/13/async-ctp-sp1-refresh.aspx –

+0

Way per andare a Microsoft! –

risposta

23

(trascritto da Ben come commento)

basta usare System.Windows.Forms.Timer. Imposta il timer per 5 secondi e gestisci l'evento Tick. Quando l'evento spara, fai la cosa.

...e disabilita il timer (IsEnabled = false) prima di eseguire il tuo lavoro in oder per sopprimere un secondo.

The Tick evento possono essere eseguito su un altro thread che non può modificare la vostra interfaccia grafica, si può prendere questo:

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 */ 
       } 
      } 
     } 
    } 

Solo per completezza: con async/attendono, si può ritardare l'esecuzione qualcosa molto facile (un solo colpo, non ripetere l'invocazione):

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(); 
} 

Per ulteriori informazioni, vedere "async e aspettare" in MSDN.

+0

Voto in aumento, bella spiegazione completa. – Ben

+1

'.BeginInvoke (myTimer_Tick, sender, e)' non funziona perché "Argument type metodo group non è assegnabile a system.delegate –

+1

PS: se l'intervallo non fosse lungo come 5 secondi avresti bisogno di un blocco per evitare un secondo tick da eseguire prima che il primo avesse la possibilità di disabilitare il timer – mtijn

2

l'applicazione si blocca perché si sta richiamando il 5 secondi di attesa/attesa sul thread dell'interfaccia utente principale. metti l'azione sleep/wait/any in un thread separato (in realtà System.Windows.Forms.Timer dovrebbe farlo per te) e quando viene completata richiama l'azione che trasforma alcuni controlli in verde. ricorda di controllare InvokeRequired. ecco una breve campione (SetText può essere chiamato da un altro thread, se è la chiamata sarà invece invocato sul thread dell'interfaccia utente principale in cui la casella di testo è acceso):

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; 
} 
} 

ho preso il campione da here (vale la pena una lettura!).

+0

Come aggirare l'operazione "Cross-thread non valida: il pulsante 'Controllo' a cui si accede da un thread diverso dal thread su cui è stato creato." eccezione allora? –

+1

Vedere Control.Invoke – Onkelborg

+1

Invoke is magic. Grande! –

0

Penso che quello che stai cercando è System.Timers ... Non penso che sia necessario impostare un timer CWND per ciò che si sta tentando di fare: dare un'occhiata a http://msdn.microsoft.com/en-us/library/0tcs6ww8(v=VS.90).aspx per un esempio. Dovrebbe fare il trucco.

+0

System.Windows.Forms.Timer, non sta utilizzando una discussione. –

8

È possibile avviare un'attività asincrona che svolge l'azione:

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

[EDIT]

Per superare l'intervallo in è sufficiente per memorizzare in una variabile:

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

[/ EDIT]

+0

Ciò funzionerebbe. anche se penso di voler passare l'intervallo. –

+1

Non è una buona idea. Qui rapisci un PoolThread per 5 secondi. E quindi basta usare Invoke() per uscire dal thread. –

+0

@TonyHopkinson Ho esteso l'esempio per passare l'intervallo. – PVitt

1

Stai sbagliando. Fare clic sul pulsante per avviare un timer con un intervallo di x secondi. Quando questi sono in su, l'eventhandler esegue l'operazione.

Quindi cosa non vuoi che succeda.

Mentre i secondi x sono in corso.?

Mentre l'attività è in esecuzione?

Se ad esempio si desidera che il pulsante non venga premuto finché il ritardo e l'attività non vengono completati. Disabilitarlo nel gestore di clic del pulsante e abilitarlo al completamento dell'attività.

Se tutto ciò che si desidera è un ritardo di cinque secondi prima dell'attività, è necessario passare il ritardo di avvio all'attività e lasciarlo occupare.

0

@eFloh nel post contrassegnato come risposta ha detto:

The Tick evento può essere eseguito su un altro thread che non può modificare vostra interfaccia grafica, si può prendere questo ...

Non è quello che dicono i dottori.
Si sta utilizzando un System.Windows.Forms.Timer nel codice di esempio.
Questo è un Forms.Timer.
In base ai documenti C# gli eventi timer vengono generati sul thread dell'interfaccia utente.

Questo timer di Windows è progettato per un ambiente a thread singolo in cui i thread di interfaccia utente vengono utilizzati per eseguire l'elaborazione. E 'necessario che il codice utente hanno una pompa messaggio interfaccia utente disponibile e sempre funzionano dallo stesso filo ...

vedere anche stackoverflow post here

Problemi correlati