views:

6579

answers:

9

With the following file reading code:

using (FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.None))
{
    using (TextReader tr = new StreamReader(fileStream))
    {
     string fileContents = tr.ReadToEnd();
    }
}

And the following file write code:

using (TextWriter tw = new StreamWriter(fileName))
{
    tw.Write(fileContents);
    tw.Close();
}

The following exception details are seen:

The process cannot access the file 'c:\temp\myfile.txt' because it is being used by another process.

What is the best way of avoiding this? Does the reader need to retry upon receipt of the exception or is there some better way?

Note that the reader process is using a FileSystemWatcher to know when the file has changed.

Also note that, in this instance, I'm not looking for alternatives ways of sharing strings between the 2 processes.

+2  A: 

You can use a Mutex object for this.

leppie
Thanks for the quick answer. What if the processes are on different systems and the file is being shared over the network?
Iain
Hehe, that wont work then. Use a zero-byte 'lock' file like Linux services does then :)
leppie
Or better still write a service, and use it to control access to the file with a Mutex, This should be expanded with a sample and a global mutex cause the "popular" answer to this question at the moment is really risky code.
Sam Saffron
+8  A: 

You can open a file for writing and only lock write access, thereby allowing others to still read the file.

For example,

using (FileStream stream = new FileStream(@"C:\Myfile.txt", FileMode.Open, FileAccess.ReadWrite, FileShare.Read))
{
   // Do your writing here.
}

Other file access just opens the file for reading and not writing, and allows readwrite sharing.

using (FileStream stream = new FileStream(@"C:\Myfile.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
   // Does reading  here.
}

If you want to ensure that readers will always read an up-to-date file, you will either need to use a locking file that indicates someone is writing to the file (though you may get a race condition if not carefully implemented) or make sure you block write-sharing when opening to read and handle the exception so you can try again until you get exclusive access.

Jeff Yates
But in this case, if I understand the workings correctly, you could read an incomplete file?
Mitchel Sellers
That is true, yes. That's always going to be the case if you allow more than one consumer to access the file.The only way to achieve this and synchronize is to either use a lock file, or to catch the access exception and try again until you get exclusive access.
Jeff Yates
+4  A: 

If you create a named Mutex you can define the mutex in the writing application, and have the reading application wait until the mutex is released.

So in the notification process that is currently working with the FileSystemWatcher, simply check to see if you need to wait for the mutex, if you do, it will wait, then process.

Here is a VB example of a Mutex like this that I found, it should be easy enough to convert to C#.

Mitchel Sellers
+1  A: 

Get your process to check the status of the file if it is being written to. You can do this by the presence of a lock file (i.e. the presence of this other file, which can be empty, prevents writing to the main file).

Even this is not failsafe however, as the two processes may create the lock file at the same time - but you can check for this before you commit the write.

If your process encounters a lock file then get it to simply sleep/wait and try again at a predefined interval in the future.

Anthony
+1  A: 

Write to a temp file, when finished writing rename/move the file to the location and/or name that the reader is looking for.

KristoferA - Huagati.com
+3  A: 

Is there any particular reason for opening the file with FileShare.None? That'll prevent the file from being opened by any other process.

FileShare.Write or FileShare.ReadWrite should allow the other process (subject to permissions) to open and write to the file while you are reading it, however you'll have to watch for the file changing underneath you while you read it - simply buffering the contents upon opening may help here.

All of these answers, however, are equally valid - the best solution depends on exactly what you're trying to do with the file: if it's important to read it while guaranteeing it doesn't change, then lock it and handle the subsequent exception in your writing code; if it's important to read and write to it at the same time, then change the FileShare constant.

symonc
A: 

The reader and writer both need retry mechanisms. Also FileShare should be set to FileShare.read for the readers and FileShare.none for the writer. This should ensure that the readers don't read the file while writing is in progress.

The reader (excluding retry) becomes

using (FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
    using (TextReader tr = new StreamReader(fileStream))
    {
        string fileContents = tr.ReadToEnd();
    }
}

The writer (excluding retry) becomes:

FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
using (TextWriter tw = new StreamWriter(fileStream))
{
    tw.Write(fileContents);
    tw.Close();
}
Iain
A: 

static double GetFileSize(string path) {

FileInfo info = new FileInfo(path);

        if (!info.Exists)
            return 0;

        return (double)info.Length;
    }

I have mentioned an example above. Where you can use it? For instance: I create XML file using XMLWriter class which writes elements in XML file, during the writing of file I use FileInfo class to verify the file size, it gives me the access to get the length of file, while writing the elements in that file.

Hey Rais, welcome to StackOverflow! It's probably worth asking your question as a whole new question otherwise noone will see it!
Iain
A: 

What if you have no control over the process that is writing to the file and you only have control over the process that is reading. How do you deal with this problem then? I have tryed the above but it still will not work for my problem.

Hey dban10, it's probably worth asking your question as a new question or adding as a comment to an existing answer. Otherwise noone will see your question.
Iain