J'utilise Visual Studio 2010 pour écrire une application GUI C#/.NET simple, dans laquelle j'utilise une classe Logger
pour écrire des informations de suivi/débogage dans un seul fichier à partir de tous les différents classes du projet. (Voir le code source ci-dessous.)Plusieurs threads accédant à l'objet singleton dans VS2010
Le constructeur de chaque classe écrit une entrée dans le journal lorsque l'un de ses types d'objets est instancié. L'une de ces classes est un composant de contrôleur GUI personnalisé (classe FileAttributesCtl
), qui est contenu dans un couple des formulaires GUI utilisés par le programme.
Le problème que j'ai est que deux fichiers journaux sont créés, environ 200 msec. Le premier fichier journal contient (uniquement) un message indiquant qu'un objet FileAttributesCtl
a été construit et le second contient tous les autres messages écrits dans le flux de sortie du fichier journal (supposé) partagé. Donc, chaque fois que j'exécute mon code de projet, j'obtiens deux fichiers journaux.
étrange encore, chaque fois que je reconstruire mon projet (F6), un fichier journal est créé pour l'objet FileAttributesCtl
, ce qui indique qu'un objet de contrôle de ce type est en fait instancié au cours du processus de construction.
Cela a apparemment quelque chose à voir avec le filetage. Si le fichier journal n'est pas nommé de manière unique (si je n'ajoute pas de chaîne de date/heure unique au nom de fichier), j'obtiens une exception, indiquant que plusieurs processus (qui est en fait le processus VS2010) utilisent actuellement le fichier. Donc, ma question est: Comment puis-je obtenir l'objet singleton pour être un seul objet?
Une question secondaire est: pourquoi le VS2010 agit-il de cette manière?
//----------------------------------------
// Logger.cs
class Logger
{
// Singleton object
private static Logger s_logger =
new Logger("C:/Temp/foo.log");
public static Logger Log
{
get { return s_logger; }
}
private TextWriter m_out;
private Logger(string fname)
{
// Add a date/time suffix to the filename
fname = ...;
// Open/create the logging output file
m_out = new StreamWriter(
new FileStream(fname, FileMode.Create, FileAccess.Write,
FileShare.Read));
m_out.WriteLine(DateTime.Now.ToString(
"'$ 'yyyy-MM-dd' 'HH:mm:ss.fff"));
}
...
}
//----------------------------------------
// FileAttributesCtl.cs
public partial class FileAttributesCtl: UserControl
{
private Logger m_log = Logger.Log;
public FileAttributesCtl()
{
m_log.WriteLine("FileAttributesCtl()"); //Written to first logfile
InitializeComponent();
}
...
}
//----------------------------------------
// FileCopyForm.cs
public partial class FileCopyForm: Form
{
private Logger m_log = Logger.Log;
public FileCopyForm()
{
// Setup
m_log.WriteLine("FileCopyForm()"); //Written to second logfile
// Initialize the GUI form
m_log.WriteLine("FileCopyGui.InitializeComponent()");
InitializeComponent();
...
}
...
}
Note: Ceci est très similaire à une question de Décembre 2009:
Access to singleton object from another thread
mais il n'a pas les réponses à ma question.
Mise à jour
Une enquête plus approfondie montre que le VS2010 est instancié en effet le composant personnalisé lors de la construction, probablement pour qu'il puisse le rendre dans la fenêtre Designer.
En outre, il existe en effet deux threads distincts appelant le constructeur Logger
(chacun ayant un ManagedThreadID
différent). L'utilisation d'un initialiseur de classe statique pour construire l'objet singleton ne fonctionne pas; J'ai toujours deux fichiers journaux.
Résolution
Un examen plus approfondi, je remarque que le contrôle personnalisé est de s'instancié deux fois, et ce qui est montré dans les deux fichiers journaux.
Par conséquent, je pense que le problème est entièrement dû au fait que VS instancie l'objet de contrôle personnalisé avant d'exécuter le programme qui entraîne la création du premier fichier journal. Le deuxième fichier journal est ensuite créé après que le programme a démarré l'exécution normale.
Ainsi, le premier fichier journal est simplement un effet secondaire du processus de construction et n'a rien à voir avec l'exécution de plusieurs threads pendant l'exécution normale du programme.
La solution évidente consiste à supprimer tout le code d'effet secondaire du fichier journal des constructeurs de composants. Ou tout simplement ignorer complètement le premier fichier journal.
Vous devriez mieux télécharger log4net pour faire le travail :) – Zebi
Est-ce que quelqu'un sait si 'log4net' est immunisé à ce problème? –
Je ne sais pas à propos de immunitaire, mais de leur FAQ ... Est-ce que log4net thread-safe? Oui, log4net est thread-safe. –