我正在使用Visual Studio 2010编写一个简单的C/.NET GUI应用程序,其中我使用
Logger
类将跟踪/调试信息从项目的所有类中写入单个文件。(请参阅下面的源代码。)
当每个类的一个对象类型被实例化时,它的构造函数向日志中写入一个条目。其中一个类是自定义的GUI控制器组件(类
FileAttributesCtl
,它包含在程序使用的几个GUI表单中。
我遇到的问题是创建了两个日志文件,间隔大约200毫秒。第一个日志文件包含(仅)一条消息,
文件属性目录
对象已被构造,第二个包含所有其他写入(假定)共享日志文件输出流的消息。所以每次执行项目代码时,我都会得到两个日志文件。
更奇怪的是,每次我重建项目(F6)时,都会为
文件属性目录
对象,指示实际实例化了此类型的控件对象
在构建过程中
.
这显然与线程有关。如果日志文件的名称不是唯一的(即,如果我没有在文件名后面附加一个唯一的日期/时间字符串),我会得到一个异常,表明有多个进程(实际上是VS2010进程本身)正在使用该文件。
所以我的问题是:如何让单例对象实际上是一个单例对象?
第二个问题是:为什么VS2010是这样的?
//----------------------------------------
// 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();
...
}
...
}
注:
这与2009年12月的一个问题非常相似:
Access to singleton object from another thread
但我的问题没有答案。
更新
进一步的调查显示,VS2010确实在构建期间实例化了自定义组件,可能是为了在设计器窗口中呈现它。
此外,确实有两个单独的线程调用
记录器
构造器(每个都有不同的
ManagedThreadID
)
使用静态类初始值设定项构造singleton对象不起作用;我仍然得到两个日志文件。
分辨率
仔细检查后,我注意到自定义控件被实例化了两次,这两个日志文件都显示了。
因此,我认为问题完全是由于vs实例化了自定义控件对象
在执行程序之前
这将导致创建第一个日志文件。然后创建第二个日志文件
程序开始正常执行后
.
因此,第一个日志文件只是构建过程的副作用,与正常程序操作期间执行的多个线程没有任何关系。
显而易见的解决方案是从组件构造函数中删除所有日志文件的副作用代码。或者直接忽略第一个日志文件。