【问题标题】:File being used by another process using StreamWriter另一个进程正在使用 StreamWriter 使用的文件
【发布时间】:2013-12-01 03:31:33
【问题描述】:

我的程序对我来说是练习,但是,当我尝试编写它找到的所有目录时,它崩溃了。

我尝试了以下方法:

  • 让它写入文件流而不是文件本身
  • 使用 File.Writealllines 使用列表(这有效,只有前五个没有更多)
  • FileStream.Write(subdir.ToCharArray())

我不明白为什么这不起作用,我做错了什么?

static void Main(string[] args)
{
    Method(@"C:\");
}

static void Method(string dir)
{
    //crash happens here v
    StreamWriter sw = new StreamWriter(@"C:\users\"+Environment.UserName+"\desktop\log.txt",true);

    foreach (string subdir in Directory.GetDirectories(dir))
    {
        try
        {
            Console.WriteLine(subdir);
            sw.Write(subdir);
            Method(subdir);
        }
        catch (UnauthorizedAccessException)
        {
            Console.WriteLine("Error");
        }
    }
  sw.Close(); 
}

【问题讨论】:

    标签: c# streamwriter


    【解决方案1】:

    我同意上述观点,但在我的情况下,解决方案略有不同。

        private static object locker = new object();
    
        private static void WriteMessageToFile(string message)
        {
            string dateStr = DateTime.Now.Date.Day.ToString()+"_"+ DateTime.Now.Date.Month.ToString()+"_"+ DateTime.Now.Date.Year.ToString();
            if (!Directory.Exists("Logs"))
            {
                DirectoryInfo di = Directory.CreateDirectory("Logs");
            }
    
            //Guid guidGenerator = Guid.NewGuid();
            string filePath = _env.ContentRootPath + "\\Logs\\ProcessLog_" + dateStr + ".txt";
            FileInfo fi = new FileInfo(filePath);
    
            lock (locker)
            {
                using (FileStream file = new FileStream(fi.FullName, FileMode.Append, FileAccess.Write, FileShare.Read))
                using (StreamWriter streamWriter = new StreamWriter(file))
                {
                    streamWriter.WriteLine(message);
                    streamWriter.Close();
                }
            }
    
        }
    

    因为下面这个函数在我的asp.net核心应用中很多地方都调用了异步和异步。在这种情况下,一个线程试图写入一个文件,另一个线程想要写入同一个文件,结果出现了错误。作为解决方案,我尝试了上述方法,但它也不起作用,因为我试图在关闭前一个流之前打开一个新流。所以我决定编写一个安全的代码块作为解决方案。在这种情况下,由于其他线程无法到达锁定区域,他们通过等待前面的操作进行写入操作,我能够正确写入文件。

    我认为;背后还有另一个原因代码,因为我在启动时使用了单例注册。这个函数的调用者类是相互隔离的。由于这个原因,他们不知道之前调用了哪个线程的函数。他们的一生已经结束了。 FileStream 还包装了 StreamWriter ,那么它也可以在没有锁定的情况下工作,无论如何它是有保证的。

    即使是 Microsoft.Extensions.Logging 默认也不支持 FileLoger,但我们可以编写自定义。我在下面分享整个实现

    public class FileLoger : ILogger
        {
            public static IHostingEnvironment _env;
            private static object locker = new object();
    
            public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
            {
                var message = string.Format("{0}: {1} - {2}", logLevel.ToString(), eventId.Id, formatter(state, exception));
                WriteMessageToFile(message);
            }
            private static void WriteMessageToFile(string message)
            {
                string dateStr = DateTime.Now.Date.Day.ToString()+"_"+ DateTime.Now.Date.Month.ToString()+"_"+ DateTime.Now.Date.Year.ToString();
                if (!Directory.Exists("Logs"))
                {
                    DirectoryInfo di = Directory.CreateDirectory("Logs");
                }
    
                //Guid guidGenerator = Guid.NewGuid();
                string filePath = _env.ContentRootPath + "\\Logs\\ProcessLog_" + dateStr + ".txt";
                FileInfo fi = new FileInfo(filePath);
    
                lock (locker)
                {
                    using (FileStream file = new FileStream(fi.FullName, FileMode.Append, FileAccess.Write, FileShare.Read))
                    using (StreamWriter streamWriter = new StreamWriter(file))
                    {
                        streamWriter.WriteLine(message);
                        streamWriter.Close();
                    }
                }
    
            }
            public IDisposable BeginScope<TState>(TState state)
            {
                return null;
            }
            public bool IsEnabled(LogLevel logLevel)
            {
                return true;
            }
        }
    
        public class FileLogProvider : ILoggerProvider
        {
    
            public FileLogProvider(IHostingEnvironment env)
            {
                FileLoger._env = env;
            }
    
            public ILogger CreateLogger(string category)
            {
                return new FileLoger();
            }
            public void Dispose()
            {
    
            }
        }
    

    【讨论】:

    • 对于未来的人,您能否详细说明为什么您的解决方案需要有所不同。根据您对锁的使用,我想这是由于多线程造成的?
    【解决方案2】:

    看来你在再次使用之前没有关闭你的streamwriter

     public static void Method(string dir)
        {
            //crash happens here v
            StreamWriter sw = new StreamWriter(@"C:\users\"+Environment.UserName+"\desktop\log.txt",true);
    
            foreach (string subdir in Directory.GetDirectories(dir))
            {
    
                try
                {
    
                    Console.WriteLine(subdir);
                    sw.Write(subdir);
                    //This line you'll call "Method" again
                    Method(subdir);
    
                }
                catch (UnauthorizedAccessException)
                {
                    Console.WriteLine("Error");
                }
            }
          sw.Close(); 
        }
    

    另外,另一个建议,你为什么不使用“System.IO.File.AppendAllText(Path,Text)”方法?使用起来更方便

    【讨论】:

      【解决方案3】:

      它是递归的。

      因为你又在这里打电话给Method

      Console.WriteLine(subdir);
      sw.Write(subdir);
      Method(subdir); // BOOM
      

      您的文件已经打开。你不能再打开它来写了。

      打开Main中的文件一次..

      static void Main(string[] args) {
          using (StreamWriter sw = new StreamWriter(@"C:\users\"+Environment.UserName+"\desktop\log.txt",true)) {
              Method(@"C:\", sw);
          }
      }
      

      然后在你的方法中接受它:

      public static void Method(string dir, StreamWriter sw) {
      

      然后当你再次调用它时:

      sw.Write(subdir);
      Method(subdir, sw); // pass in the streamwriter.
      

      但请注意,您将很快开始消耗内存。您正在遍历整个 C:\ 驱动器。也许在一个较小的文件夹上测试它?

      【讨论】:

        猜你喜欢
        • 2013-01-05
        • 2013-05-15
        • 1970-01-01
        • 2014-12-10
        • 1970-01-01
        • 1970-01-01
        • 2021-12-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多