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!");
}
}
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). –
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. –
@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. –