You need to read or write data to or from a section of a file, and you want to make sure that no other processes or threads can access, modify, or delete the file until you have finished with it.
Locking
out other processes or threads from accessing your file while you are
using it is accomplished through the Lock
method
of the FileStream
class. The following code
creates a file from the fileName
parameter
and writes two lines to it. The entire file is then locked using the
Lock
method. While the file is locked, the code
goes off and does some other processing; when this code returns, the
file is closed, thereby unlocking it:
public void CreateLockedFile(string fileName) { FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite); StreamWriter streamWriter = new StreamWriter(fileStream); streamWriter.WriteLine("The First Line"); streamWriter.WriteLine("The Second Line"); streamWriter.Flush( ); // Lock all of the file fileStream.Lock(0, fileStream.Length); // Do some lengthy processing here... Thread.Sleep(1000); // Make sure we unlock the file. // If a process terminates with part of a file locked or closes a file // that has outstanding locks, the behavior is undefined which is MS // speak for bad things.... fileStream.Unlock(0, fileStream.Length); streamWriter.WriteLine("The Third Line"); streamWriter.Close( ); fileStream.Close( ); }
If a file is opened within your application and the
FileShare
parameter of the
FileStream.Open
call is set to
FileShare.ReadWrite
or
FileShare.Write
, other code in your application
can alter the contents of the file while you are using it. To handle
file access with more granularity, use the Lock
method on the FileStream
object to prevent other
code from overwriting all or a portion of your file. Once you are
done with the locked portion of your file, you can call the
Unlock
method on the
FileStream
object to allow other code in your
application to write data to the file or that portion of the file.
To lock an entire file, use the following syntax:
fileStream.Lock(0, fileStream.Length);
To unlock a portion of a file, use the following syntax:
fileStream.Lock(4, fileStream.Length - 4);
This line of code locks the entire file except for the first four characters. Note that you can lock an entire file and still open it multiple times, as well as write to it.
If another thread is accessing this file, it is possible to see an
IOException
thrown during the call to either the
Write
, Flush
, or
Close
methods. For example, the following code is
prone to such an exception:
public void CreateLockedFile(string fileName) { FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite); StreamWriter streamWriter = new StreamWriter(fileStream); streamWriter.WriteLine("The First Line"); streamWriter.WriteLine("The Second Line"); streamWriter.Flush( ); // Lock all of the file fileStream.Lock(0, fileStream.Length); StreamWriter streamWriter2 = new StreamWriter(new FileStream(fileName, FileMode.Open, FileAccess.Write, FileShare.ReadWrite)); streamWriter2.Write("foo "); try { streamWriter2.Close( ); // --> Exception occurs here! } catch { Console.WriteLine("The streamWriter2.Close call generated an exception."); } streamWriter.WriteLine("The Third Line"); streamWriter.Close( ); fileStream.Close( ); }
Even though streamWriter2
, the second
StreamWriter
object, writes to a locked file, it
is when the streamWriter2.Close
method is executed
that the
IOException
is thrown.
If the code for this recipe were rewritten as follows:
public void CreateLockedFile(string fileName) { FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite); StreamWriter streamWriter = new StreamWriter(fileStream); streamWriter.WriteLine("The First Line"); streamWriter.WriteLine("The Second Line"); streamWriter.Flush( ); // Lock all of the file fileStream.Lock(0, fileStream.Length); // Try to access the locked file... StreamWriter streamWriter2 = new StreamWriter(new FileStream(fileName, FileMode.Open, FileAccess.Write, FileShare.ReadWrite)); StreamWriter2.Write("foo "); streamWriter.Close( ); streamWriter2.Flush( ); streamWriter2.Close( ); streamWriter.Close( ); fileStream.Close( ); }
no exception is thrown. This is due to the fact that the code closed
the FileStream
object that initially locked the
entire file. This action also freed all of the locks on the file that
this FileStream
object was holding onto. Since the
streamWriter2.Write("Foo")
method had written
Foo
to the stream’s buffer (but
had not flushed it), the string Foo
was still
waiting to be flushed and written to the actual file. Keep this
situation in mind when interleaving the opening, locking, and closing
of streams. Mistakes in code sometimes manifest themselves a while
after they are written. This leads to some bugs more difficult to
track down, so tread carefully when using file locking.