Back to all posts

Avoiding File Locks and Cannot Access File Exceptions

Posted on Jan 22, 2009

Posted in category:
Development
C#

While working through some issues recently with some legacy code written by someone else I was faced with a horrible case of a shared object that was writing to the file. This was a "custom" logging implementation and opened a file for append, inserted the line, and then closed the file. Well in times of heavy load the system would encounter errors such as "Cannot access ___ because it is being used by another process". So in effect, the file was either still open, or the lock was not yet released. This post goes through a bit of the detail on how I resolved the issue.

The Issue

The main manifestation of this issue was with a series of very aggressive log writes 100+ calls in a row. So for testing, I created an application that would write out 2000+ entries one after another to simulate heavy load. The previous developers had included a "retry" process to get around the issue. If the file couldn't be opened, it pauses 10 milliseconds then re-tried. For a 5000 record set, the app would average 1800 retries. This process was clunky at best and involved exception handling at every attempt.

My Fix

I decided to look at a locking scenario and came up with the following minimized code.

My C# Fix
Imports System.IO
Imports System.Threading

Public Class FileWriteTest
    Private _filePath As String = "C:\Testing\myfile.txt"
    Private _fileMutex As New Mutex()

    Public Sub WriteLine(ByVal sMsg As String)
        'Wait until free
        _fileMutex.WaitOne()
        Dim ofs As FileStream
        Try
            ofs = New FileStream(_filePath, FileMode.Append, FileAccess.Write, FileShare.None)
            Dim oFile As New StreamWriter(ofs)
            Dim sLine As String = sMsg & ControlChars.NewLine
            oFile.Close()
            ofs.Close()
        Catch e As Exception
            Throw
        End Try
        Finally
            'Release the mutex to allow others to call
            _fileMutex.ReleaseMutex()
        End Try
    End Sub
End Class

Now, this fix was a very simple I created a Mutex inside of the class that actually did the file operations, and then used WaitOne() to ensure that I could be the only one accessing the code, then after all file operations were complete, including the closing of the file, I release the mutex. This removed the need for a continual "retry" method as was previously used and improved performance by over 35%.

Now, the Mutex is a much more severe and costly operation than a SyncLock, but due to the nature of the code that I am working with, I couldn't get a SyncLock to work the way it needed to, thus the Mutex solution.

Conclusion

I hope that this example has helped someone out as I know when I was looking for solutions, I found it VERY hard to find any solution other than a "keep trying" approach, which to me just wasn't an option! Feel free to share comments below, and if you have any specific implementation questions please feel free to post to the forum!