web-dev-qa-db-fra.com

Comment attendre de manière asynchrone pendant x secondes et exécuter quelque chose alors?

Je sais qu'il y a Thread.Sleep et System.Windows.Forms.Timer et Monitor.Wait en C # et Windows Forms. Je n'arrive tout simplement pas à comprendre comment attendre X secondes puis faire autre chose - sans verrouiller le fil.

J'ai un formulaire avec un bouton. Lorsque vous cliquez sur un bouton, une minuterie doit démarrer et attendre 5 secondes. Après ces 5 secondes, un autre contrôle du formulaire est de couleur verte. Lors de l'utilisation de Thread.Sleep, l'application entière ne répondrait plus pendant 5 secondes - alors comment puis-je simplement "faire quelque chose après 5 secondes"?

23
Dennis G

(transcrit de Ben comme commentaire)

utilisez simplement System.Windows.Forms.Timer. Réglez la minuterie pendant 5 secondes et gérez l'événement Tick. Lorsque l'événement se déclenche, faites la chose.

... et désactivez la minuterie (IsEnabled = false) avant de faire votre travail afin de supprimer une seconde.

L'événement Tick peut être exécuté sur un autre thread qui ne peut pas modifier votre interface graphique, vous pouvez attraper ceci:

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

Juste pour être complet: avec async/wait, on peut retarder l'exécution de quelque chose de très facile (un coup, ne jamais répéter l'invocation):

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

Pour plus d'informations, voir "asynchroniser et attendre" dans MSDN.

35
eFloh

As-tu essayé

public static Task Delay(
    int millisecondsDelay
)

Vous pouvez utiliser comme ceci:

await Task.Delay(5000);

référence: https://msdn.Microsoft.com/en-us/library/hh194873 (v = vs.110) .aspx

13
macio.Jun

Vous pouvez démarrer une tâche asynchrone qui exécute votre action:

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

[MODIFIER]

Pour passer l'intervalle, il vous suffit de le stocker dans une variable:

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

[/ EDIT]

10
PVitt

Vous pouvez attendre le fil d'interface comme vous le souhaitez.

Task.Factory.StartNew(async() =>
{
    await Task.Delay(2000);

    // it only works in WPF
    Application.Current.Dispatcher.Invoke(() =>
    {
        // Do something on the UI thread.
    });
});
1
Masuri

votre application se bloque parce que vous invoquez la mise en veille/attente de 5 secondes sur le thread d'interface utilisateur principal. placez l'action sleep/wait/any dans un thread séparé (en fait, System.Windows.Forms.Timer devrait le faire pour vous) et quand il se termine, appelez l'action qui transforme certains contrôles en vert. n'oubliez pas de vérifier InvokeRequired. voici un court échantillon (SetText peut être appelé à partir d'un autre thread, si c'est l'appel sera à la place appelé sur le thread d'interface utilisateur principal où la zone de texte est activée):

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

J'ai pris l'échantillon de ici (vaut bien une lecture!).

1
mtijn

Vous le voyez mal. Cliquez sur le bouton, il démarre une minuterie avec un intervalle de x secondes. Lorsque ceux-ci sont activés, le gestionnaire d'événements exécute la tâche.

Alors qu'est-ce que tu ne veux pas arriver.

Pendant que les x secondes s'écoulent.?

Pendant l'exécution de la tâche?

Si, par exemple, c'est que vous ne voulez pas que le bouton soit cliqué jusqu'à ce que le délai et la tâche soient terminés. Désactivez-le dans le gestionnaire de clic de bouton et activez-le à la fin de la tâche.

Si tout ce que vous voulez, c'est un délai de cinq secondes avant la tâche, vous devez transmettre le délai de démarrage à la tâche et le laisser s'en occuper.

1
Tony Hopkinson

@ eFloh dans le message marqué comme réponse dit:

L'événement Tick peut être exécuté sur un autre thread qui ne peut pas modifier votre interface graphique, vous pouvez l'attraper ...

Ce n'est pas ce que disent les docs.
Vous utilisez un System.Windows.Forms.Timer dans votre exemple de code.
C'est un Forms.Timer .
Selon les documents C #, les événements Timer sont déclenchés sur le thread d'interface utilisateur.

Ce minuteur Windows est conçu pour un environnement à thread unique où les threads d'interface utilisateur sont utilisés pour effectuer le traitement. Cela nécessite que le code utilisateur dispose d'une pompe de message UI disponible et fonctionne toujours à partir du même thread ...

Voir aussi stackoverflow post ici

1
deegee

Je pense que ce que vous recherchez est System.Timers ... Je ne pense pas que vous ayez besoin de définir une minuterie CWND pour ce que vous essayez de faire: jetez un œil à http://msdn.Microsoft.com/en-us/library/0tcs6ww8 (v = VS.90) .aspx pour un exemple. Cela devrait faire l'affaire.

0
Ahmed Masud