系统.IO.IOException:由于正在被其他进程使用,无法访问文件“.txt”。

3
我正在使用以下代码记录 web 应用程序的错误。
using (StreamWriter myStream = new StreamWriter(sLogFilePath, true))
{                
myStream.WriteLine(string.Format("{0, -45}{1, -25}{2, -10 {3}", guid, DateTime.Now, StringEnum.GetStringValue(enumMsg), sText));      

}

有时会出现以下异常:“System.IO.IOException:由于正在被另一个进程使用,因此无法访问文件'.txt'。”。
我认为这是由于同时存在多个 web 应用程序实例引起的。请帮我解决这个问题,好吗?
编辑:我必须添加,对于每个我记录的方法,都会像这样:
日期 - 方法 X 已启动。
日期 - 异常消息(未找到表或其他错误)
日期 - 方法 X 已停止。
当出现此错误时,只记录如下内容:
日期 - System.IO.IOException: 由于正在被另一个进程使用,因此无法访问文件'.txt'。
4个回答

4
很遗憾,Windows不允许等待文件锁。为了解决这个问题,您的所有应用程序都需要创建一个锁,以供涉及的所有进程进行检查。
使用此代码仅可防止单个进程中的线程访问该文件:
/* Suitable for a single process but fails with multiple processes */
private static object lockObj = new Object();
lock (lockObj)
{
    using (StreamWriter myStream = new StreamWriter(sLogFilePath, true))
    {                
        myStream.WriteLine(string.Format("{0, -45}{1, -25}{2, -10 {3}", guid, DateTime.Now, StringEnum.GetStringValue(enumMsg), sText));      
    }
}

为了在多个进程之间进行锁定,需要使用 Mutex 锁。这为锁定命名,其他进程可以检查它。它的工作方式如下:
/* Suitable for multiple processes on the same machine but fails for
   multiple processes on multiple machines */
using (Mutex myMutex = new Mutex(true, "Some name that is unlikly to clash with other mutextes", bool))
{
    myMutex.WaitOne();
    try
    {
        using (StreamWriter myStream = new StreamWriter(sLogFilePath, true))
        {
            myStream.WriteLine(string.Format("{0, -45}{1, -25}{2, -10 {3}", guid, DateTime.Now, StringEnum.GetStringValue(enumMsg), sText));
        }
    }
    finally
    {
        myMutex.ReleaseMutex();
    }

我不认为Mutexes可以从远程机器访问,所以如果你在文件共享上有一个文件,且正在尝试从多台计算机上的进程写入它,那么最好在托管该文件的机器上编写服务器组件来协调进程之间的操作。


3
假设你需要每个线程最终写入日志,你可以锁定关键部分。您可以锁定关键部分。
private static object fileLock = new Object();
...
lock (fileLock)
{
    using (StreamWriter myStream = new StreamWriter(sLogFilePath, true))
    {                
        myStream.WriteLine(string.Format("{0, -45}{1, -25}{2, -10 {3}", guid, DateTime.Now, StringEnum.GetStringValue(enumMsg), sText));      

    }
}

这意味着在任何给定时间只有1个线程可以写入文件,其他线程将被阻塞,直到当前线程退出关键部分(此时文件锁将被删除)。
需要注意的是,lock进程级别的,因此如果您的网站正在运行Web农场/ Garden的上下文中,则需要查看系统范围的锁定机制,即Mutexes

问题在于Web服务器往往会在多个进程以及多个线程中运行站点。此处给出的锁只适用于一个进程。 - Martin Brown
@MartinBrown "Web服务器倾向于在多个进程中运行站点" - 仅当在Web农场/花园的上下文中运行时。这是一个有效的观点,但考虑到OP没有提到运行方式,我认为他们不是在这样做。 - James
他们在回收过程中也这样做。旧进程和新进程之间存在重叠,而旧进程会完成在回收开始之前启动的请求。 - Martin Brown
@MartinBrown 嗯,这是真的,我没有从那个角度考虑过它。到那时,就变成了概率 - 一个线程在回收之前锁定文件并且另一个线程在回收后写入文件(同时仍然被旧进程锁定)的机会有多大 - 这很可能永远不会发生....但仍有可能。我认为在单个Web服务器环境中,互斥锁实际上只是保险措施,因为进程重叠的机会极小。 - James

3

你的Web服务器将在多个线程中运行请求。如果两个或更多的请求同时需要记录异常,这将导致您看到的异常。

你可以像James建议的那样锁定该部分,或者使用一个日志框架来处理多线程问题,例如Lgo4net或者NLog


Web服务器可能会在多个进程和多个线程中运行站点。在Web Garden的情况下,可能会永久地运行,或者在回收进程时短暂运行。 - Martin Brown

-1

我已经将这段代码添加到我的类中:

 public static bool IsFileLocked(FileInfo file)
        {
            FileStream stream = null;

            try
            {
                stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
            }
            catch
            {
                return true;
            }
            finally
            {
                if (stream != null)
                {
                    stream.Close();
                }
            }

            return false;
        }

现在我的LogToFile方法是这样的:

while (IsFileLocked(fi))
            {
            }

            using (StreamWriter myStream = new StreamWriter(sLogFilePath, true))
            {
                if (displayTime == true)
                    myStream.WriteLine(string.Format("{0, -45}{1, -25}{2, -10}{3}", guid, DateTime.Now, StringEnum.GetStringValue(enumMsg), sText));
                else
                    myStream.WriteLine(string.Format("{0, -70}{1, -10}{2} ", guid, StringEnum.GetStringValue(enumMsg), sText));                
            }

我希望这个能够运行。


它可能会改善情况,但并没有完全解决。原因是在调用IsFileLocked和创建StreamWriter之间,另一个进程可能已经打开了该文件。 - Martin Brown
你可能会遇到的另一个问题是,在非常高的负载下,一个线程可能会阻塞另一个线程。这是因为没有队列来确保等待线程最终得到查看。一个线程可以释放文件上的锁,并在另一个线程的检查之间需要它。此外,由于这是轮询文件,在每次尝试之间几乎没有暂停,等待线程很可能会使用大量CPU。 - Martin Brown

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接