12
votes

Déterminer ce qui bloque le fil de l'interface utilisateur

Je travaille sur une application à temps réel plutôt grande .NET WPF. L'application fonctionne bien et comme prévu, à l'exception d'une grande émission - UI Mise à jour est lente.

Cette application est fortement entraînée par l'événement, il existe des événements soulevés pour toutes sortes de choses - à travers ces événements, l'interface utilisateur est mise à jour.

Un ou plusieurs de ces événements bloquent l'interface utilisateur d'afficher immédiatement. Lorsque tout le travail est terminé, l'interface utilisateur montre les résultats attendus.

Y a-t-il un moyen de déterminer quel gestionnaire d'événements provoque le goulot d'étranglement?


0 commentaires

4 Réponses :


6
votes

Avez-vous accès à un profileur de code? C'est le type de chose qu'ils sont bons. Je recommande d'obtenir un si la réponse est non.

En plus d'utiliser un profileur. Vous pouvez faire du profilage "pauvre homme" en plaçant des déclarations de timing au début et à la fin des blocs de code que vous soupçonnez. Vous pouvez même utiliser des points d'arrêt et le temps avec une horloge murale. Le problème se produit-il lorsque vous cliquez sur quelque chose? Si oui, commencez là. Est-ce un problème récurrent sans interaction utilisateur? Commencez avec les minuteries alors.

comme pour résoudre réellement le problème ... à moins que le gestionnaire incriminé ne fait quelque chose qui puisse être rendu plus efficace, envisagez d'adopter une approche multi-threadée. La nouvelle bibliothèque de tâches pour .NET 4.0 est vraiment incroyable à cet égard.


1 commentaires

Salut Colithium. Merci d'avoir répondu. L'application est multithreadée - faisant une utilisation du parallélisme dans mes requêtes et des boucles LINQ. De plus, au lieu de travailler avec des objets de thread, j'utilise la tâche.Factory.StartNouveau pour la multi-threading. L'application ne contient pas de minuterie ni de travailleurs d'arrière-plan. J'ai une tâche principale non bloquante (fil) qui ne contient que la logique commerciale, c'est-à-dire aucune interaction de l'interface utilisateur du tout. La logique de cette tâche prend certaines actions et soulève des événements associés à ces actions. J'utilise VS2010 - qui a un profileur. Je ne sais pas comment l'utiliser et comment évaluer les résultats.



0
votes

En tant qu'enversion de première commande, je trouve utile de casser le débogueur (avec le bouton de pause dans l'IDE) et regardez la pile. Faites-le quelques fois, et vous pouvez voir s'il y a un motif. Êtes-vous toujours dans la même fonction? Faites-vous quelque chose de cher en réponse à un événement? Avez-vous plus d'événements que vous attendez? C'est peu tech, mais peut être très efficace.


0 commentaires

12
votes

Je soutiens entièrement la suggestion de Colithium d'utiliser un profileur.

De plus, si le blocage prend plus d'une seconde, vous pourrez peut-être appuyer sur le bouton "Pause" dans Visual Studio. Dans la barre d'outils, il y a une liste déroulante dans laquelle vous pouvez choisir le "fil principal". Ensuite, il saute à la méthode qui bloque actuellement l'interface utilisateur.


1 commentaires

Merci heinze! J'ai essayé votre suggestion et cela a fonctionné à merveille. Très utile et astuce simple à retenir. Croyez-le ou non, c'est le journal des applications qui causait le problème. Utilisation de l'appendeur log4net pour mettre à jour une UI de journal RichTextbox.



23
votes
  public class UIBlockDetector
{
    static Timer _timer;
    public UIBlockDetector(int  maxFreezeTimeInMilliseconds = 200)
    {
        var sw = new Stopwatch();

        new DispatcherTimer(TimeSpan.FromMilliseconds(10), DispatcherPriority.Send, (sender, args) =>
        {
            lock (sw)
            {
                sw.Restart();
            }

        }, Application.Current.Dispatcher);

        _timer = new Timer(state =>
        {
            lock (sw)
            {
                if (sw.ElapsedMilliseconds > maxFreezeTimeInMilliseconds)
                {
                    // Debugger.Break() or set breakpoint here;
                    // Goto Visual Studio --> Debug --> Windows --> Theads 
                    // and checkup where the MainThread is.
                }
            }

        }, null, TimeSpan.FromMilliseconds(0), TimeSpan.FromMilliseconds(10));

    }

}
Just new this class in MainWindow constructor. When the breakpoint hits, you can go to Visual Studio --> Debug --> Windows --> Threads and check what operation blocked your UI-Thread!

6 commentaires

NOUVELLE LA CLASSE À UNE PLCE DE VOTRE CHOIX + Définissez un point d'arrêt à l'intérieur du "Si (SW.LapSedMillisecondes> MaxfreezetimeInmillisecondes)". Cette classe n'est qu'une logique de surveillance pour la maintenance.


Quelle minuterie utilisez-vous ici?


Wow. Cela fonctionne vraiment vraiment génial. Nice là-bas. Je vais le garder pratique.


L'un des extraits les plus utiles que j'ai jamais conservés! merci@andreas


@Andreas est 10 millisecondes allant causer des goulots d'étranglement sur le fil principal du Dispatchertimer? Une raison pour laquelle vous avez choisi cette valeur?


C'est génial et m'a aidé immensément. Merci!