2010-11-08 9 views
0

Je rencontre des problèmes avec un thread de traitement en C#. Fondamentalement, le thread gère les fenêtres de chat lorsque de nouveaux messages arrivent ou sont envoyés et, malheureusement, j'ai des situations différentes se produisent en fonction de l'environnement en cours d'exécution.Application.Run() fonctionne différemment dans une version Release sans débogueur que dans une version Debug

Lors de l'exécution d'une version Debug (avec ou sans débogueur) ou d'une version Release sous un débogueur, la fonction Process() fonctionne correctement, affiche les fenêtres et reçoit les messages correctement. Cependant, lors de l'exécution d'une version Release sans débogueur, l'appel Application.Run() semble arrêter le traitement du thread principal Process() (remarquez que cet appel se produit sous un sous-thread du thread de traitement) et donc plus de traitement se produit. Grâce à l'utilisation de l'appel MessageBox.Show() j'ai déterminé que Application.Run() est le dernier appel à faire avant que plus de boîtes de message sont affichées (et ils devraient être comme il montre combien de messages sont reçu à chaque fois que la boucle while est exécutée).

Est-ce que quelqu'un sait pourquoi l'appel Application.Run() se comporte différemment dans cette situation?

/// <summary> 
    /// Processes the IM message queue, managing the chat windows and messages. 
    /// </summary> 
    private void Process() 
    { 
     try 
     { 
      MessageBox.Show("MessageQueue process has started!"); 

      while (this.m_Running) 
      { 
       List<Message> messages = null; 
       lock (this.m_Lock) 
       { 
        messages = new List<Message>(this.m_Messages); 
        MessageBox.Show("MessageQueue received " + this.m_Messages.Count + " messages on this spin."); 
        this.m_Messages.Clear(); 
       } 

       // Process all the messages 
       foreach (Message m in messages) 
       { 
        Contact c = m.Contact; 

        if (!this.m_Windows.Keys.Contains(c.ID) || this.m_Windows[c.ID] == null) 
        { 
         MessageBox.Show("MessageQueue is creating a new window."); 
         bool complete = false; 
         Thread t = new Thread(() => 
          { 
           try 
           { 
            ChatWindow w = new ChatWindow(this, c, new Contact(this.m_Client.JID, null)); 
            w.Load += (sender, e) => 
             { 
              if (m.IsTo) 
               w.AppendSentMessage(m.To, m.Data); 
              else if (m.IsFrom) 
               w.AppendRecievedMessage(m.From, m.Data); 

              w.UpdateStatus(c); 
             }; 
            w.FormClosed += (sender, e) => 
             { 
              this.m_Windows[c.ID] = null; 
             }; 
            c.StatusUpdated += (sender, e) => 
             { 
              RoketPack.Manager.VoidLambda lambda =() => 
              { 
               w.UpdateStatus(c); 
              }; 

              if (w.InvokeRequired) 
               w.Invoke(lambda); 
              else 
               lambda(); 
             }; 
            MessageBox.Show("MessageQueue is now showing the new window."); 
            w.Show(); 
            if (!this.m_Windows.Keys.Contains(c.ID)) 
             this.m_Windows.Add(c.ID, w); 
            else 
             this.m_Windows[c.ID] = w; 
            complete = true; 
            MessageBox.Show("MessageQueue is now running the new window."); 
            Application.Run(w); 
            MessageBox.Show("MessageQueue is now closing the window."); 
           } 
           catch (Exception ex) 
           { 
            MessageBox.Show(ex.ToString()); 
            complete = true; 
           } 
          }); 
         t.Name = "IM Chat Window - " + c.ID; 
         t.IsBackground = true; 
         t.Start(); 

         // We have to wait until the form has been added to the dictionary. 
         while (!complete) ; 
        } 
        else 
        { 
         RoketPack.Manager.VoidLambda lambda =() => 
          { 
           if (m.IsTo) 
            this.m_Windows[c.ID].AppendSentMessage(m.To, m.Data); 
           else if (m.IsFrom) 
            this.m_Windows[c.ID].AppendRecievedMessage(m.From, m.Data); 
           MessageBox.Show("MessageQueue appended the message to the chat window."); 
          }; 

         MessageBox.Show("MessageQueue received a message and is now forwarding it onto the chat window."); 
         if (this.m_Windows[c.ID].InvokeRequired) 
          this.m_Windows[c.ID].Invoke(lambda); 
         else 
          lambda(); 
        } 
       } 

       // Sleep for 10 milliseconds. 
       Thread.Sleep(10); 
      } 
     } 
     finally 
     { 
      MessageBox.Show("MessageQueue process has terminated!"); 
     } 
    } 

Répondre

1

En dehors de ce leppie écrit, cela ressemble à un mauvais point de départ:

while (!complete); 

Je ne sais pas exactement ce qui garantit il y a environ hissa variables et la visibilité, mais-bouclage serré est presque toujours une mauvaise idée. Il est généralement préférable d'utiliser une approche Wait/Notify ou une Auto/ManualResetEvent.

+0

Oui, ce n'est probablement pas optimal, mais ce n'est pas non plus la cause du problème car la boîte de message est affichée une fois que complete a été défini sur true (et donc cette boucle sera interrompue). –

+0

J'ai changé le code pour utiliser une approche Wait/Notify et w.ShowDialog() à la place et il semble avoir résolu le problème. Je crois que c'était le problème des optimisations CLR, qui peuvent causer des problèmes tels que le booléen complet étant mis à vrai avant que la valeur du dictionnaire soit ajouté comme mentionné dans http://www.albahari.com/threading/part4.aspx#_Memory_Barriers_and_Volatility (ce qui provoquera bien sûr le crash du thread). L'ajout de l'approche Attendre/Notifier impliquait implicitement une barrière de mémoire à travers les instructions lock {} qui sont cependant nécessaires. –

+0

@Hach - rien à voir avec une barrière de mémoire, c'est là que le mot-clé * volatile * doit être utilisé. Mais ne le faites pas, un ARE est supérieur. –

1

Est-ce que vous vous rendez compte Application.Run ne retourne pas jusqu'à ce que votre application se ferme?

Il semble également que vous appelez Application.Run à partir d'un thread enfant.

+0

La description de Application.Run est "Commence à exécuter une boucle de message d'application standard sur le * thread en cours *, et rend le formulaire spécifié visible." Cela implique une indépendance vis-à-vis de tout autre thread et c'est exactement ce qui se passe lors de l'exécution d'une compilation Debug ou lors de l'exécution d'une version Release sous un débogueur. La seule exception est lorsque vous l'exécutez sous une version Release sans un débogueur, et c'est à ce moment-là qu'il semble que ce soit à l'échelle de l'application. –

+0

Je devrais ajouter que sans l'appel Application.Run(), la forme créée se bloque simplement parce qu'il n'y a pas de boucle de message qui est traitée pour elle une fois que le thread se termine. –

Questions connexes