我正在使用Visual Studio 2010编写一个简单的C#/ .NET GUI应用程序,其中我使用Logger
类将跟踪/调试信息写入来自所有各种类的单个文件中。项目。 (参见下面的源代码。)
每个类的构造函数在实例化其中一个对象类型时将条目写入日志。其中一个类是自定义GUI控制器组件(类FileAttributesCtl
),它包含在程序使用的几种GUI表单中。
我遇到的问题是创建了两个日志文件,相隔大约200毫秒。第一个日志文件(仅)包含已构造FileAttributesCtl
对象的消息,第二个日志文件包含写入(据称)共享日志文件输出流的所有其他消息。所以每次执行我的项目代码时,我都会得到两个日志文件。
奇怪的是,每次重建我的项目(F6)时,都会为FileAttributesCtl
对象创建一个日志文件,表明此类型的控件对象在构建过程中实际实例化
这显然与线程有关。如果日志文件没有唯一命名(即,如果我没有在文件名中附加唯一的日期/时间字符串),我会得到一个异常,表明多个进程(实际上是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确实在构建过程中实例化了自定义组件,可能是因为它可以在Designer窗口中呈现它。
此外,确实有两个单独的线程调用Logger
构造函数(每个都有不同的ManagedThreadID
)。
使用静态类初始化程序构造单例对象不起作用;我仍然得到两个日志文件。
解决
仔细检查后,我注意到自定义控件已实例化两次,并且这两个日志文件中都会显示。
因此我认为问题完全是由于VS在执行程序之前实例化自定义控件对象导致第一个日志文件被创建。然后在程序开始正常执行后创建第二个日志文件。
因此,第一个日志文件只是构建过程的副作用,并且与正常程序操作期间执行的多个线程没有任何关系。
显而易见的解决方案是从组件构造函数中删除所有日志文件副作用代码。或者只是完全忽略第一个日志文件。
答案 0 :(得分:1)
很可能Visual Studio正在构建您的UI组件(在设计器中显示),并且在此过程中,您的构造函数被调用,这就是您在构建过程中看到该日志文件的原因。
答案 1 :(得分:1)
静态数据+线程=麻烦
您需要同步对单例的访问(以及单例的初始化)。
静态构造函数可以提供帮助
class Logger
{
private static Logger
static Logger()
{
s_logger = new Logger("C:/Temp/foo.log");
}
// ...
或者更好的是使用一个记录库(log4net)来处理所有这些东西。
对于VS构建导致创建日志,我并不感到惊讶。它可能是通过反射来实例化表格以发现有关表单的信息。
每条评论更新
@LoadMaster“静态类初始值设定项没有 工作。我在日志文件中添加了更多信息 输出包括当前线程 ManagedThreadID,果然, 有两种不同的线程ID 创建两个日志文件。“
这很奇怪。每MSDN
类的静态构造函数 在给定的内容中最多执行一次 应用领域。执行一个 静态构造函数由。触发 发生以下事件中的第一个 在应用程序域中:
- 创建了一个类的实例。
- 班级的任何静态成员 被引用。
您的主题必须已移动AppDomains,或者您的代码段中缺少某些代码。