C#文件正在被另一个进程使用。

5
我不确定如何解决我的问题。有时我会遇到错误:“由于其他进程正在使用该文件,因此无法访问文件'xxxx'”。这是发生错误的方法:
```html

Here is my method where the error happens:

```
private static void write_history(int index, int time_in_sec, int[] sent_resources)
        {
            string filepath = "Config\\xxx.txt";
            int writing_index = 0;

            if (File.Exists(filepath))
            {
                System.Threading.Thread.Sleep(5000);
                StreamReader reader = new StreamReader(new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.Read));
                string temp = reader.ReadToEnd();
                reader.Close();

                for (int i = 0; i < 20; i++)
                {
                    if (temp.IndexOf("<hst_" + i.ToString() + ">") == -1)
                    {
                        writing_index = i;
                        break;
                    }
                }
            }

            System.Threading.Thread.Sleep(5000);
            // write to the file
            StreamWriter writer = new StreamWriter(filepath, true);
            writer.WriteLine("<hst_" + writing_index.ToString() + ">" + DateTime.Now.AddSeconds(time_in_sec).ToString() + "|" + sent_resources[0] + "|" + sent_resources[1] + "|" + sent_resources[2] + "|" + sent_resources[3] + "</hst_" + writing_index.ToString() + ">");
            writer.Close();
        }

我收到的错误信息:

************** Exception Text **************
System.IO.IOException: The process cannot access the file 'Config\\xxx.txt' because it is being used by another process.
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath)

4
尝试将你的StreamReader放在一个using块中。 - tnw
你是否正在快速构建/重建项目?或者经常中止程序? - user195488
你尝试过使用StreamReader.Close()吗?- MSDN - http://msdn.microsoft.com/zh-cn/library/system.io.streamreader.close.aspx - aked
@Brad 我同意,我猜这只是一次半吊子的尝试来解决手头的问题。楼主你必须移除那部分代码... 在那个方法里睡上整整10秒钟,除非你有一个非常充分的理由在那里放它,否则就太荒谬了。 - tnw
1
@simplecoder 嗯...他的代码里已经有reader.Close();了。 - tnw
显示剩余3条评论
5个回答

9
如果您已确认正确打开和关闭文件,则最可能的罪魁祸首是您的病毒检测器。病毒检测器会观察到日志文件已更改,打开它以搜索病毒,然后在病毒检查器正在读取文件时,尝试写入该文件将失败。
如果是这种情况,那么我建议您询问您的病毒检查器供应商推荐的解决方法是什么。

2
好主意。如果您认为可能是这种情况,值得运行Process Explorer并检查是否是这种情况。 - tnw

3
  1. Use using around all your objects that are IDisposable. using will ALWAYS call the method Dispose, even if there is an exception.
  2. You did close your reader, but did not close the filestream.
  3. This code can be made much shorter, see my second example at the bottom of my answer.

    private static void write_history(int index, int time_in_sec, int[] sent_resources)
    {
        string filepath = "Config\\xxx.txt";
        int writing_index = 0;
    
        if (File.Exists(filepath))
        {
            System.Threading.Thread.Sleep(5000);
            using(FileStream stream = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.Read)
            using(StreamReader reader = new StreamReader(stream))
            {
                 string temp = reader.ReadToEnd();
            }
    
            for (int i = 0; i < 20; i++)
            {
                if (temp.IndexOf("<hst_" + i.ToString() + ">") == -1)
                {
                    writing_index = i;
                    break;
                }
            }
        }
    
        System.Threading.Thread.Sleep(5000);
        // write to the file
        using(StreamWriter writer = new StreamWriter(filepath, true))
        { 
             writer.WriteLine("<hst_" + writing_index.ToString() + ">" + DateTime.Now.AddSeconds(time_in_sec).ToString() + "|" + sent_resources[0] + "|" + sent_resources[1] + "|" + sent_resources[2] + "|" + sent_resources[3] + "</hst_" + writing_index.ToString() + ">");
        }
    }
    

简化版:

    private static void write_history(int index, int time_in_sec, int[] sent_resources)
    {
        string filepath = "Config\\xxx.txt";
        int writing_index = 0;

        if (File.Exists(filepath))
        {
            System.Threading.Thread.Sleep(5000);
            string temp = File.ReadAllText(filepath);

            for (int i = 0; i < 20; i++)
            {
                if (temp.IndexOf("<hst_" + i.ToString() + ">") == -1)
                {
                    writing_index = i;
                    break;
                }
            }
        }

        System.Threading.Thread.Sleep(5000);
        // write to the file
        File.WriteAllText(filepath, "<hst_" + writing_index.ToString() + ">" + DateTime.Now.AddSeconds(time_in_sec).ToString() + "|" + sent_resources[0] + "|" + sent_resources[1] + "|" + sent_resources[2] + "|" + sent_resources[3] + "</hst_" + writing_index.ToString() + ">");
    }

在多线程情况下:

private static readonly object _syncLock = new object();

private static void write_history(int index, int time_in_sec, int[] sent_resources)
{
   lock(_syncLock)
   {
        string filepath = "Config\\xxx.txt";
        int writing_index = 0;

        if (File.Exists(filepath))
        {
            System.Threading.Thread.Sleep(5000);
            string temp = File.ReadAllText(filepath);

            for (int i = 0; i < 20; i++)
            {
                if (temp.IndexOf("<hst_" + i.ToString() + ">") == -1)
                {
                    writing_index = i;
                    break;
                }
            }
        }

        System.Threading.Thread.Sleep(5000);
        // write to the file
        File.WriteAllText(filepath, "<hst_" + writing_index.ToString() + ">" + DateTime.Now.AddSeconds(time_in_sec).ToString() + "|" + sent_resources[0] + "|" + sent_resources[1] + "|" + sent_resources[2] + "|" + sent_resources[3] + "</hst_" + writing_index.ToString() + ">");
    }
 }

谢谢。你的回答很符合我的问题。我不确定这是否有效,但我会尝试一下。还要感谢您解释了使用语句。(无法测试如此快,就像我说的有时发生,有时不发生:o) - Doctorslo
我测试了一下,又崩溃了 :( - Doctorslo
@DoctorSlo:昨天我问了:你的函数在多线程环境中被调用了吗?能告诉我答案吗? - Martin Mulder
@DoctorSlo:如果您正在使用多线程环境,则我添加了另一种同步的解决方案。 - Martin Mulder
@Doctorslo:又想到一件事:您是否运行多个此应用程序的实例?如果是,只在一台机器上还是多台机器上? - Martin Mulder

1
你可以使用 "using" 或者强制垃圾回收器释放所有引用。这解决了我的问题。在应用进程级或线程级更改之前,请检查您的代码。
示例:
using(StreamWriter writer....)
{
  Your Code....
}
this.deleteFiles(filepath);

或者:

GC.Collect();
this.deleteFiles(filepath);

1
我猜测你的FileStream(传入StreamReader构造函数的那个)没有被关闭。
StreamReader reader = new StreamReader(new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.Read));

将该语句放在 using 语句中,以确保所有的端点都被连接起来。
using(StreamReader reader = new StreamReader(new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.Read)))
{
    //the using statement will handle the closing for you
}

1
虽然在这里使用“using”肯定是最佳实践,但我想知道它如何解决问题。OP确实关闭了读取器,关闭读取器会关闭底层流,进而处理流。因此,假设没有任何异常阻止代码到达关闭语句(OP没有提到任何异常),那么using语句如何帮助解决问题? - Matthias

1
你的方法不是线程安全的。如果你以多线程方式访问此方法,你的线程可能会同时尝试访问文件。除了alexn的答案中正确处理你的StreamReader外,还应该考虑在类中的方法之外使用静态对象,然后在访问文件之前仅锁定它。
private static object lockObject = new object();

private static void write_history(int index
{
    lock(lockObject)
    {
        // Access file here
    }
}

嗯,我想这是有效的,尽管 OP 没有提到任何关于多线程的事情。我不会 -1 但我也不会 +1。 - tnw
此外,我没有看到任何在多个线程之间共享的内容(对于文件不需要锁定,因为如果您尝试多次写入相同的文件,则已经会收到异常)。 - I4V
今天我假设一个历史写入静态方法可以从多个线程中调用。这就是为什么我说“如果你以多线程方式访问此方法”的原因。 这总结了我的假设并为答案设置了上下文。我不确定我理解您评论I4V的观点。如果您想要两个对文件的写入都成功,那么必须锁定访问点。 我并不是说锁定文件,我在谈论线程锁定代码。 也许您误解了我的意思? - Tombala
谢谢,但这个方法只在一个线程中使用。我相信多线程不是问题。 - Doctorslo
我曾经遇到过同样的问题。但是现在它已经正常工作了,不再使用这种方法。非常感谢@Tombala。 - Nayan Dey

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