请问,如何解决多线程访问同一个文件句柄、资源的冲突问题?谢谢!比如:如下类,是用于记录程序日志的。我调用共用的Log方法来记录程序中的任何日志。这在单线程的应用中没有任何问题。
但是,在多线程的调用中,也就是几个不同的线程同时对LogHelpers进行Log方法调用时,sw对象会出现error。看上去像是一个线程正在对文件写操作时,另一个线程已经把sw对象给关闭了。所以,有的时候,这一句报错。
sw.WriteLine(Line, arg);MSG:          Cannot write to a closed TextWriter.
STACK:           at System.IO.__Error.WriterClosed()
   at System.IO.StreamWriter.Flush(Boolean flushStream, Boolean flushEncoder)
   at System.IO.StreamWriter.Write(Char[] buffer, Int32 index, Int32 count)
   at System.IO.TextWriter.WriteLine(String value)
   at System.IO.TextWriter.WriteLine(String format, Object[] arg)
Type:         System.ObjectDisposedException
THROWN BY:    Void WriterClosed()
请问,如何修改这个类的代码,来避免这种情况?
要求,每调用一次Log方法,必须1)打开文件;2)写文件;3)关闭文件;这个不能变。
另外,不同的线程对这个类的Log方法调用顺序,时间,频率是不可知的。请问,C#,有没有简单的锁保护机制(语句)可以用于解决这种冲突?请提供示例代码,谢谢!用lock语句可以解决吗?
lock (syncRoot)
{
}
下面是用于记录程序日志的类Class LogHelpers
{
   private static StreamWriter sw;
   private static string fileName="c:\testlogs.log";
   private static long maxFileSize=65535;
   
   protected static void WriteLine(string Line, params object[] arg)
   {
                try
                {
                    // Write to log
                    if (sw != null) sw.WriteLine(Line, arg);
                }
                catch (System.ObjectDisposedException ex)
                {
                    OpenLogFile();
                    // Write to log
                    if (sw != null) sw.WriteLine(Line, arg);                }   }   private static void OpenLogFile()
   {
                try
                {
                    if (sw != null)
                        sw.Close();
                    fi = new FileInfo(fileName);
                    if (!fi.Directory.Exists)
                        Directory.CreateDirectory(fi.DirectoryName);
                    if (fi.Exists && fi.Length < maxFileSize)
                        if (encrypted)
                            sw = CreateEncrypted(FileMode.Append);
                        else
                            sw = File.AppendText(fileName); // Append to the file.
                    else if (encrypted)
                        sw = CreateEncrypted(FileMode.Create);
                    else
                        sw = File.CreateText(fileName); // Clears the log.                    // TODO Autoflush?
                    // We have to use Autoflash for calculating the file size
                    // in current implementation, see BeginLog
                    sw.AutoFlush = true;
                }
                catch
                {
                    //do nothing.
                }
   }   protected static void BeginLog()
   {
                if (sw == null)
                    OpenLogFile();                fi = new FileInfo(fileName);
                if (fi.Length > maxFileSize)
                {
                    // rename file to .prev:
                    string prev = fileName + ".prev";
                    if (File.Exists(prev))
                        File.Delete(prev);                    sw.Close();
                    fi.MoveTo(prev);                    // reopen new file:
                    OpenLogFile();
                }
   }   protected static void EndLog()
   {
                if (sw != null) 
                {
                    //TODO: keep open? close? use a pool of writers?
                    sw.Close();
                    sw = null;
                }
   }   public static void Log(string str)
   {
                BeginLog();                WriteLine("* * * * * * * * * * * *");
                WriteLine(String.Format("{0, -13}", "DATE & TIME: ") +
                          DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss:ff tt"));
                WriteLine(str);
                WriteLine("");                EndLog();
   }
}

解决方案 »

  1.   

    lock住就可以了但这样会导致性能降低,建议你先开缓存,等缓存等到一定量时,开一个额外线程去写磁盘。
      

  2.   

    你看下EnterpriseLibrary里的Log的block,可以按它的思路来做。
      

  3.   

    不知道改成这样行不行? private static object syncRoot = new Object();
     public static void Log(string str) 
      { 
         Lock (syncRoot)
         {
                    BeginLog();                 WriteLine("* * * * * * * * * * * *"); 
                    WriteLine(String.Format("{0, -13}", "DATE & TIME: ") + 
                              DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss:ff tt")); 
                    WriteLine(str); 
                    WriteLine("");                 EndLog(); 
         }
      } 
      

  4.   

    还是应该这样?
    public static void Log(string str) 
      { 
        Lock (sw) 
        { 
                    BeginLog();                 WriteLine("* * * * * * * * * * * *"); 
                    WriteLine(String.Format("{0, -13}", "DATE & TIME: ") + 
                              DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss:ff tt")); 
                    WriteLine(str); 
                    WriteLine("");                 EndLog(); 
        } 
      } 
      

  5.   

    没有回答你的问题,但建议不用自己写日志类,一是要写好不容易,二是已经有很多现成的了。比如System.Diagnostics.Trace类。
    你可以在程序的开头指定log到一个文件:    TextWriterTraceListener logWriter = new TextWriterTraceListener( @"c:\temp\log.txt" );
        Trace.Listeners.Add( logWriter );
    也可以事后在程序的配置文件中进行配置(见System.Diagnostics.Trace类)写日志更简单:    Trace.TraceWarning("Cannot open file: {0}", "abc.txt");
      

  6.   


        ReaderWriterLock RWL = new ReaderWriterLock();
        RWL.AcquireWriterLock(1000);
      

  7.   

    搂主可以把日志记录到Windows事件日志表里,比你写文件好多了,查阅也方便(Windows事件查看器)
    在.NET里面用EventLog对象来访问,很方便