为什么我会收到有关文件正在被另一个进程使用的 IO 异常,而实际上并非如此?



我已经实现了一个基本的日志记录类,当我尝试创建它的新实例时,我得到以下异常。

进程无法访问文件"C:\Users\carl\Desktop\My Projects\TCV2\CallPotential.UI\bin\Debug\application.log",因为它正被另一个进程使用。

下面是记录器类的代码。

using System;
using System.IO;
using System.Reflection;
namespace CallPotential.Utilities
{
    public class Logger : IDisposable
    {
        /// <summary>
        /// Used to write output to the log file.
        /// </summary>
        private StreamWriter _stream;
        /// <summary>
        /// The absolute path to the log files location.
        /// </summary>
        public String LogFileName
        {
            get
            {
                // get the directory where our main assembly is located.
                Assembly assembly = Assembly.GetExecutingAssembly();
                String directoryName = Path.GetDirectoryName(assembly.Location);
                return Path.Combine(directoryName, "application.log");
            }
        }
        /// <summary>
        /// Creates a new instance of the Logger class
        /// </summary>        
        public Logger()
        {         
           _stream = new StreamWriter(LogFileName);         
        }
        /// <summary>
        /// Writes a message out to the application log file.
        /// </summary>
        /// <param name="message">The message to write to the log file.</param>
        public void Write(String message)
        {
            _stream.WriteLine(message);
            _stream.Flush();
            _stream.Close();
        }
        /// <summary>
        /// Writes a message including the applications state to the log file.
        /// </summary>
        /// <param name="state">The application state at the time of the logged message.</param>
        /// <param name="message">The message to be written to the log file.</param>
        public void Write(AppState state, String message)
        {
            Write(String.Format("{0}rnt{1}", state, message));
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        public void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_stream != null)
                {
                    _stream.Dispose();
                    _stream = null;
                }
            }
        }
        ~Logger()
        {
            Dispose(false);
        }
    }
}

请注意确定原因,但它会在构造函数中引发异常。任何帮助解决这个问题将不胜感激。

基本问题是你保持流打开,这意味着你只能访问日志记录类的一个实例,除非你做了一些更改。

您可以进行的各种更改包括:

1) 确保每个实例都生成自己的日志文件名。

2) 强制所有日志记录记录到在整个应用程序中使用的特定文件名。

无论哪种情况,您都需要控制对关键功能的访问,例如通过 SyncLock 块打开、关闭、读取和写入,以确保如果从线程进行日志记录或有多个实例共享同一文件,则只有一个调用方同时执行代码。

在第一种情况下,您可能只想拥有一个实例。

在第二种情况下,您可以使用静态流编写器和静态方法,因为您不需要创建类的新实例。这是我们多年前根据早期异常处理应用程序块中Microsoft的指导实现的方法。

当我尝试创建它的新实例时,出现以下异常。

您应该在创建新实例之前释放当前实例。

也许您应该考虑单例模式。

我总是使用 append 属性进行日志记录,所以永远不要忘记流打开

    public void Write(AppState state, String message)
    {
        StreamWriter sw = new StreamWriter(LogFileName, true);// true to append the new text
        sw.WriteLine(String.Format("{0}rnt{1}", state, message));
        sw.Close(); // Close() is the same as Dispose()
    }

我能够重现错误 - 第二行抛出异常:

var logger1 = new Logger();
var logger2 = new Logger();

但是当我在第一个实例上使用 Write() 方法时,即:

var logger1 = new Logger();
logger1.Write("XYZ");
var logger2 = new Logger();

异常不再被抛出。它的行为方式是否与您相同?

问题是您对两个记录器使用相同的文件,如果您不关闭流或释放第一个实例,您基本上是在尝试同时为一个文件打开两个流。

尝试使用两个不同的文件(例如,您可以在构造函数中传递日志文件的路径,而不是具有默认值的属性),并让我知道这是否解决了您的问题。

附言但是,如果您需要两个记录器写入同一个文件,并且它们应该能够同时访问该文件,那么您应该采取不同的方法 - 单例模式会更合适。

永远只能有 1 个记录器实例。 但是,Visual Studio 应用程序可能通过反射创建实例,因此它可以使用智能感知。 a) 关闭托管进程,b) 使用命令行编译。

解决您的特定问题,但是设计很糟糕。

最新更新