关于c#:进程无法访问该文件,因为正由另一个使用streamwriter的进程使用该文件

The process cannot access the file because it is being used by another process using streamwriter

我有两个程序:一个C GUI应用程序和一个C Windows服务访问相同的文本文件;

1
2
a) the C# GUI application will write/append to the text file
b) the windows service will copy the file to a network location every 20 mins.

当操作同时发生时,我收到如下错误消息:

1
2
3
4
5
6
7
8
9
10
2014/09/08 21:15:56 mscorlib
The process cannot access the file 'C:\09082014.log' 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)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options)
   at System.IO.StreamWriter.CreateFile(String path, Boolean append)
   at System.IO.StreamWriter..ctor(String path, Boolean append, Encoding encoding, Int32 bufferSize)
   at System.IO.StreamWriter..ctor(String path, Boolean append)
   at DataloggerUI.DataHelper.WriteDataLog(String msg, Int64& downTimeSince)
   at DataloggerUI.Form1.ReceiveData(IAsyncResult asyncResult)

----C Windows服务部分如下所示----------

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  if (File.Exists(destination + @"" + fi.Name))
            {
                FileInfo fi_dest = new FileInfo(destination + @"" + fi.Name);
                if (fi.LastWriteTime > fi_dest.LastWriteTime)
                {
                    File.Copy(fi.FullName, destination + @"" + fi.Name, true);
                    WriteLog("Send" + fi.FullName +" to server");
                }
            }
            else
            {
                File.Copy(fi.FullName, destination + @"" + fi.Name, true);
                WriteLog("Send" + fi.FullName +" to server");
            }  
}

-------C Windows GUI应用程序代码如下所示-------

1
2
3
4
5
6
    string logfile = DataHelper.GetAppConfigString("MPRS_LogDir") + @"" + DateTime.Now.ToString("MMddyyyy") +".log";
    using (StreamWriter sw = new StreamWriter(logfile, true))
    {
        sw.WriteLine(tick +"" +"KYEC" + Environment.MachineName +"" + msg);
        sw.Close();
    }

错误消息由GUI应用程序抛出。我的代码中有错误或错误的实践吗?

------------根据Peter的建议将代码修改为以下内容----------

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    try
    {
        using (StreamWriter sw = new StreamWriter(logfile, true))
        {
            sw.WriteLine(tick +"" +"KYEC" + Environment.MachineName +"" + msg);
        }
    }
    catch (IOException ex)
    {
        WriteErrorLog("IOException" + ex.Message);
        System.Threading.Thread.Sleep(2000); //2 secs                
        using (StreamWriter sw = new StreamWriter(logfile, true))
        {
            sw.WriteLine(tick +"" +"KYEC" + Environment.MachineName +"" + msg);
        }
    }


可以在共享读写模式下使用文件流同时写入和复制文件。

尝试以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 //To write file use
    using (FileStream fs = new FileStream(fileToReadPath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
    {
        using (StreamWriter StreamWriter = new StreamWriter(fs))
        {
            StreamWriter.WriteLine(tick +"" +"KYEC" + Environment.MachineName +"" + msg);
            StreamWriter.Close();
        }
    }

//To copy file use
    using (FileStream inStream = new FileStream(fileToReadPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    {
        using (FileStream outStream = File.Create(fileToWritePath))
        {
            while (inStream.Position < inStream.Length)
            {
                outStream.WriteByte((byte)inStream.ReadByte());
            }
        }
    }

这样,您就可以在不被其他进程错误使用的情况下完成任务。


根据Peter Duniho的回答和您自己的编辑,这应该是正确的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// try to write maximum of 3 times
var maxRetry = 3;
for (int retry = 0; retry < maxRetry; retry++)
{
    try
    {
        using (StreamWriter sw = new StreamWriter(logfile, true))
        {
            sw.WriteLine("{0} KYEC{1} {2}", tick, Environment.MachineName, msg);

            break; // you were successfull so leave the retry loop
        }
    }
    catch (IOException)
    {
        if(retry < maxRetry - 1)
        {
            System.Threading.Thread.Sleep(2000); // Wait some time before retry (2 secs)
        }
        else
        {
            // handle unsuccessfull write attempts or just ignore.
        }
    }
}

这使您有机会指定将重试写入尝试的时间。


你至少有几个选择。最简单的概念是将文件I/O包装在try/catch中,如果发生IOException(并且仅当发生IOException时),请短暂延迟操作(例如thread.sleep(),设置计时器),然后重试。

另一种方法是使用命名的互斥体来允许服务和GUI进程协调访问。在尝试各自的操作之前,每个都将获取互斥体;如果另一个当前正在访问该文件,则尝试获取互斥体将导致进程等待另一个操作完成。

重试代码有时会变得杂乱和冗长,因此,虽然我认为重试方法更容易理解,但我认为mutex方法实际上更易读,更容易获得正确的结果(一旦您遇到困难,学习如何创建一个命名的mutex&hellip;这并不难,而且msdn有示例)。