0
votes

Dans l'application C # Winforms, pourquoi ne pas dormir dans une boucle de temps?

Ce n'est pas un duplicata de thread.sleep () La boucle ne fonctionne pas correctement? parce que celui-ci est la réponse acceptée, c'est qu'il mêlait de millisecondes et secondes.

Note- Quelqu'un a suggéré que Gardez le fil de l'interface utilisateur réactif lorsque vous exécutez une tâche longue sous Windows Forms résoudrait ma question. L'exemple de cette question est très compliqué en ce sens qu'il a beaucoup de choses qui ne sont pas pertinentes et répondent à cela et à inclure toute cette complexité. Il est beaucoup plus difficile de comprendre cette question et cette réponse. Ma question est beaucoup plus simple et la réponse est donc beaucoup plus simple. La réponse acceptée ici n'a pas de jeton.throwifcancellationRequée (); code> Il est très difficile d'adapter la réponse complexe à ce lien, à ma question simple. Je n'accepterais certainement pas la réponse acceptée à cette question comme une réponse à la mienne. Hans a effectivement donné une réponse d'une ligne. Je ne pense pas qu'une réponse d'une ligne s'applique à cette question. Donc, ma question est différente de celle-là. P>


J'ai un formulaire avec un bouton et une étiquette p>

J'ai ce code lorsque le bouton est cliqué sur p> Un bouton Cliquez sur Appelle une fonction qui correspond à ce P>

            int a, b;           
            int i=0;
            while(i<5)
            {
                i += 1;
                Thread.Sleep(1000);
                label1.Text += "a";               
            }
            MessageBox.Show("done");


5 commentaires

Ce n'est pas la mise en mémoire tampon, la méthode Label.onpaint () ne peut pas fonctionner lorsque vous écrivez intentionnellement "Ne faites rien d'autre" Code. Ajouter label1.update (); à ce code, apprendre à utiliser une minuterie ou un thread de travailleur peut attendre plus tard.


@Hanspassant merci, c'est incroyable. Ajout d'une ligne label1.update (); droit après Label1.text + = "A"; . Vous pouvez poster cela comme une réponse. Qu'est-ce qui compterait comme "Ne faites rien d'autre" Code "? Code séquentiel dans le même thread?


@barlop pour que vous puissiez bloquer l'interface utilisateur pendant 5 secondes?


@noseratio Je voudrais simplement désactiver le bouton lorsque ce code est exécuté, empêchant ainsi l'utilisateur de cliquer sur le bouton, ce qui bloque donc cet aspect de l'interface utilisateur, le seul aspect de l'interface utilisateur qui exécute ce code.


Note latérale mais peut bien sûr utiliser la minuterie, alors comptez les tiques si (tmrblahtickcount == 5) {Timer1.Enabled = false; tmrblahtickcount = 0; Thingtobunafterticer (); }


3 Réponses :


-2
votes

Votre méthode doit renvoyer le contrôle à l'appelant pour que la pompe de message traite les événements repeaks qui rendront le nouveau texte. Vous avez deux options.

l'ancienne façon forte> p> xxx pré>

la nouvelle façon forte> p>

Pour que cela fonctionne, vous devez faire votre gestionnaire ASYNC. P>

async void SomeHandler(object sender, EventArgs e)
{
    int a, b;           
    int i=0;
    while(i<5)
    {
        i += 1;
        await Task.Delay(1000);  //The await keyword yields control to the caller
        label1.Text += "a";               
    }
    MessageBox.Show("done");
}


15 commentaires

Ou l'autre moyen - utilisez une minuterie.


J'aime la simplicité du code. Je viens de tester "l'ancienne façon" imprime les deux derniers A de Simultanément. Il devrait y avoir une pause entre chaque «A». Et je suppose que pas de pause entre le dernier "A" et fait. Donc, le seul problème avec ce code est-il imprimant simultanément les deux derniers. Pourquoi imprimerait-il les deux derniers 'a simultanément? Votre nouvelle méthode de passage n'a pas ce bug.


application.Deevents (); // contrôle de la pompe de message - techniquement, il ne "donne pas de contrôle à la boucle de message"; plutôt, Il exécute un message imbriqué Boucle avec l'interface utilisateur principale débloquée (contrairement à une boîte de dialogue modale), qui est une chose très méchante.


En ce qui concerne la version async / attendre , il faut toujours être pris pour éviter la réintensif, c'est-à-dire lorsque Quelqu'unRandler est appelé à nouveau pendant que la logique asynchrone précédente à l'intérieur, elle se passe toujours , Fyi @barlop.


NOOOOO! Non!! Non! N'utilisez pas des doigts !!!! L'ancienne façon dont vous devriez utiliser label1.invalidate ()


@Jeremythompson Très bien, il a dit que c'était l'ancienne façon et il a dit que ce n'était pas recommandé. Et il a fourni la nouvelle façon


@noeratio Comment puis-je éviter ce problème de ré-entrant que vous mentionnez? Si j'avais un booléen qui a été installé lorsque la chose est terminée et testé un booléen avant de courir le moment. Donc, même si cela faisait renoncé, je suppose que cela ne causerait pas de choc de code. Je suppose que je désactive probablement le bouton pendant que le code est exécuté. Il ne devrait donc pas rentrer du tout, cette désactivation du bouton pour empêcher la ré-entrée suffisante (lorsque le bouton est la seule chose que j'ai qui exécute ce code quand même)?


@barlop, cela dépend du flux de travail de l'interface utilisateur de votre application. Disons que l'utilisateur a cliqué sur le bouton qui invoque Quelqu'unRandler deux fois. Que voudriez-vous faire avec l'opération en attente? E.g., ignorer le nouveau clic (avec un simple drapeau)? Ou arrêter l'ancien et commencer un nouveau? Ou laissez l'ancien finition, puis commencer un nouveau? Dans ce dernier cas, il peut être aussi simple que d'utiliser semiaphoreslim.waitaSync , check Ceci . Je prends moi-même la deuxième approche: Stackoverflow.com/q/21424084/1768303


@noSeratio merci. Dans mon cas réel, le flux de travail de l'interface utilisateur est très protecteur, il clique sur le bouton qu'il désactive le bouton, jusqu'à ce que la fonction soit terminée. Donc, il n'y aurait aucun moyen d'un clic ou de deux clics du bouton en exécuter deux simultanément, alors je suppose que je suis d'accord que ça.


... Enfin, vous pouvez bloquer l'interface utilisateur principale avec une boîte de dialogue modale "Veuillez patienter", qui est très utilisateur hostile mais est toujours une option parfois, lors de la gestion du code hérité: Stackoverflow.com/a/47934541/1768303


@barlop, désactivation du bouton peut être correct aussi longtemps que vous êtes sûr qu'il n'y a pas d'autres chemins implicites à invoquer Quelqu'un an "/ code>. J'utiliserais un simple this._issomehandlerexecotting -style vérifier la ré-session intérieure intérieure Quelqu'un / code>.


@noSeratio Ah OK, la rentrinie n'est donc pas nécessairement un problème, uniquement si cette réintensif était imprévue / affrontement / n'a pas été testée. Bon à savoir. Merci


Veuillez supprimer le bit sur Application.Deevents () - Ce n'était que l'ancienne façon de VB6. Ce n'était jamais le chemin de .net.


@barlop - Re-entrancy est un problème grave avec DOEVENTS () - il se manifestera comme un bogue qui apparaît dans un code si apparemment indépendant et vous brûlerez une journée entière de débogage avant de vous rendre compte que c'était DOEVENTS () .


Je vous suggère de mettre à jour votre réponse 'COS, il semble être déroutant des gens .. Vous pouvez renommer l'ancienne façon à une mauvaise façon dont on ne devrait pas faire, c'est ce que les gens feraient dans VB6. Ou même mieux, plutôt que de dire que c'est une vieille façon, vous pouvez le changer en Label1.Update (); (selon le commentaire de Han) puis faire une note latérale que l'utilisation d'une ligne d'administration () est un vieille façon.



1
votes

L'approche asynchrone est la plus propre à mon avis.

Voici une autre approche, cependant, démontrant comment mettre à jour l'interface utilisateur avec invoke () si vous prenez une tâche plus classique / style de fil: < Pré> xxx


6 commentaires

Un appel d'incendie et d'oublier comme celui-ci à Tâche.Run est rarement une bonne idée. Il y a un iprogress <> -pattern pour cela, exemple , mais je doute que l'OP a besoin d'une fil de fond dans son cas.


Merci, c'est vraiment intéressant, btw je vois que MessageBox.Show n'a pas à être dans ce corps .Invoke


Cette réponse me montre également que sans cela .invoke corps, la ligne Button1.Enabled provoque une exception à ce sujet d'être invoquée dans un fil différent. C'est intéressant que cela m'a amené à voir que dans le cas de la méthode ASYNC, c'est le Même discussion, pas d'exception, et je vois cela de Système d'impression.Trreading.Thread.CurrentThread.Managedhead, tandis que voici ici un fil différent.


Je vois aussi que l'on peut le faire.Invoke ou bouton1.invoke (pour l'un des contrôles), "COS IT" exécute le délégué spécifié sur le fil qui détient la poignée de fenêtre sous-jacente du contrôle. " Comme mentionné Stackoverflow.com/questions/14703698/Invokedelegate


La MessageBox est dans le corps d'invocation de sorte que le bouton ne soit activé qu'après le renvoi de la boîte de dialogue.


Je ne veux pas exécuter le messagebox.show une fois que le bouton est activé. Si vous mettez la messagerie-boîte.show à l'extérieur, mais avant l'invocation du corps (plutôt qu'après), le bouton n'est activé que lorsque la boîte de dialogue a été rejetée. (Alors, vous obtenez toujours cet effet en vertu de la messageriebox.show précédant le bouton activé)



2
votes

juste une suggestion - utilisez le cadre réactif de Microsoft (RX) - Nuget system.reactive.windows.Forms.Forms code> et pop a à l'aide de system.reative.linq; code> en haut en haut de votre code. Ensuite, vous pouvez écrire:

Observable
    .Interval(TimeSpan.FromSeconds(1.0))
    .Take(5)
    .ObserveOn(this)
    .Subscribe(_ => label1.Text += "a");


0 commentaires