AppDomain和线程的意外FileTraceListener行为

时间:2014-03-20 16:34:33

标签: .net multithreading logging appdomain trace

亲爱的Stackoverflowers,

我有以下环境:

  • Windows 7,具有最新SP的64位Ultimate EN
  • 带有最新SP的Visual Studio 2013
  • 带有最新SP的.NET Framework 3.5
  • 我的控制台应用程序

目标

两个AppDomains,每个都有自己的线程,将Trace.WriteLine()消息写入两个本地文件跟踪侦听器

实施

  1. 我的控制台应用程序在App.config文件中定义了两个system.diagnostics跟踪listerns“ListenerA”和“ListenerB”。这些侦听器记录到名为A.log和B.log
  2. 的本地文本文件
  3. 我的控制台应用程序为每个跟踪侦听器创建一个AppDomain。
  4. 每个AppDomain创建一个线程。
  5. 线程工作器函数使用Trace.WriteLine()记录5条消息,如下所示:“ListenerA,16:48:49,6”(AppDomain.FriendlyName,Time,ManagedThreadID)
  6. 文件跟踪侦听器继承抽象System.Diagnostics.TraceListener类。
  7. 资源:

    的App.config

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <startup>
        <supportedRuntime version="v2.0.50727"/>
      </startup>
    
      <system.diagnostics>
        <trace autoflush="true" indentsize="0">
          <listeners>
            <remove name="Default"/>
            <add name="ListenerA" type="TraceListeners.Listener, TraceListenersConsole" initializeData="A.log"/>
            <add name="ListenerB" type="TraceListeners.Listener, TraceListenersConsole" initializeData="B.log"/>
          </listeners>
        </trace>
      </system.diagnostics>
    
    </configuration>
    

    的TraceListener

    using System.Diagnostics;
    using System.IO;
    
    namespace TraceListeners
    {
        public class Listener : TraceListener
        {
            private FileStream theFileStream;
    
            private string _initializeData;
    
            public Listener(string initializeData)
            {
                this._initializeData = initializeData;
    
                this.theFileStream = GetStreamWriter(Path.Combine(@"C:\temp\TraceLog", initializeData));
            }
    
    
            public override void Write(string message)
            {
                this.WriteToFile(message);
            }
    
            public override void Flush()
            {
                this.theFileStream.Flush();
            }
    
            public override void WriteLine(string message)
            {
                this.Write(message + "\r\n");
            }
    
            public override bool IsThreadSafe
            {
                get
                {
                    return false;
                }
            }
    
            private void WriteToFile(string message)
            {
                byte[] buffer = System.Text.Encoding.Default.GetBytes(message);
                this.theFileStream.Write(buffer, 0, buffer.Length);
            }
    
            private FileStream GetStreamWriter(string path)
            {
                return new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
            }
        }
    }
    

    static void Main

    using System;
    using System.Diagnostics;
    using System.Threading;
    
    namespace TraceListenersConsole
    {
        class Program
        {
            private static AppDomain _domainA;
            private static AppDomain _domainB;
    
            static void Main(string[] args)
            {
                AppDomainSetup domainSetupA = new AppDomainSetup
                {
                    ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
                    ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
                };
    
                AppDomainSetup domainSetupB = new AppDomainSetup
                {
                    ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
                    ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
                };
    
                _domainA = AppDomain.CreateDomain("ListenerA", AppDomain.CurrentDomain.Evidence, domainSetupA);
                _domainA.DoCallBack(new CrossAppDomainDelegate(AppDomainCallback));
    
                _domainB = AppDomain.CreateDomain("ListenerB", AppDomain.CurrentDomain.Evidence, domainSetupB);
                _domainB.DoCallBack(new CrossAppDomainDelegate(AppDomainCallback));
    
                //AppDomainCallback();
                //AppDomainCallback();
    
                Console.ReadKey();
            }
    
            static private void AppDomainCallback()
            {
                ThreadPool.QueueUserWorkItem(ThreadWorker);
            }
    
            static private void ThreadWorker(object obj)
            {
                for (int i = 0; i < 5; ++i)
                {
                    int threadID = Thread.CurrentThread.ManagedThreadId;
    
                    Trace.WriteLine(String.Format("{0}, {1}, {2}", AppDomain.CurrentDomain.FriendlyName, DateTime.Now.ToLongTimeString(), threadID));
                    Trace.Flush();
                    Console.WriteLine(threadID);
    
                    Thread.Sleep(1000);
                }
            }
        }
    }
    

    预期行为 日志文件A.log和B.log每个应包含十个日志条目,如下所示:

    ListenerA, 16:48:49, 6
    ListenerA, 16:48:50, 6
    ListenerA, 16:48:51, 6
    ListenerA, 16:48:52, 6
    ListenerA, 16:48:53, 6
    ListenerB, 16:48:49, 7
    ListenerB, 16:48:50, 7
    ListenerB, 16:48:51, 7
    ListenerB, 16:48:52, 7
    ListenerB, 16:48:53, 7
    

    实际行为:

    A.log包含:

    ListenerB, 17:06:40, 12
    ListenerB, 17:06:41, 12
    ListenerA, 17:06:42, 11
    ListenerB, 17:06:43, 12
    ListenerB, 17:06:44, 12
    

    B.log包含:

    ListenerB, 17:06:40, 12
    ListenerB, 17:06:41, 12
    ListenerA, 17:06:42, 11
    ListenerB, 17:06:43, 12
    ListenerB, 17:06:44, 12
    

    备注:

    • 如果我省略AppDomain创建并调用AppDomainCallback 直接起作用然后一切都按预期工作。

    • 我正在分析以这种方式使用AppDomain,thread和TraceLitener的生产问题。

    为什么日志文件只包含5个条目?为什么没有像预期的10个条目?

    非常感谢你的时间!

    最好的问候, Siraf

0 个答案:

没有答案