代码之家  ›  专栏  ›  技术社区  ›  David R Tribble

在VS2010中访问单实例对象的多个线程

  •  0
  • David R Tribble  · 技术社区  · 14 年前

    我正在使用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实例化了自定义控件对象 在执行程序之前 这将导致创建第一个日志文件。然后创建第二个日志文件 程序开始正常执行后 .

    因此,第一个日志文件只是构建过程的副作用,与正常程序操作期间执行的多个线程没有任何关系。

    显而易见的解决方案是从组件构造函数中删除所有日志文件的副作用代码。或者直接忽略第一个日志文件。

    2 回复  |  直到 14 年前
        1
  •  1
  •   BFree    14 年前

    很可能是因为Visual Studio正在构建UI组件(在设计器中显示),在此过程中,会调用构造函数,这就是为什么在构建过程中看到日志文件的原因。

        2
  •  1
  •   µBio    14 年前

    静态数据+线程=故障

    您需要同步对singleton的访问(以及singleton的初始化)。

    静态构造函数可能有帮助

    class Logger
    {
        private static Logger
        static Logger()
        {
            s_logger = new Logger("C:/Temp/foo.log");
        }
    
        // ...
    

    或者最好使用日志库( log4net )为你处理所有这些事情。

    对于导致创建日志的vs构建,我并不感到惊讶。它可能正在实例化表单,以便通过反射发现有关表单的信息。

    按注释更新

    @loadmaster“静态类初始值设定项没有 工作。我在日志文件中添加了更多信息 包含当前线程的输出 管理线程,当然, 有两个不同的线程ID 正在创建两个日志文件。“

    真奇怪。每 MSDN

    类的静态构造函数 执行 在给定的时间内最多一次 应用程序域 . 执行 静态构造函数由 以下事件中的第一个发生 在应用程序域中:

    • 将创建类的实例。
    • 类的任何静态成员 被引用。

    您的线程必须已移动AppDomain,或者代码段中缺少某些代码。