January 22, 2009

Avoiding File Locks and Cannot Access File Exceptions

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 pause 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.

   1:  Imports System.IO
   2:  Imports System.Threading
   4:  Public Class FileWriteTest
   5:      Private _filePath As String = "C:\Testing\myfile.txt"
   6:      Private _fileMutex As New Mutex()
   9:      Public Sub WriteLine(ByVal sMsg As String)
  10:      'Wait until free
  11:          _fileMutex.WaitOne()
  12:          Dim ofs As FileStream
  13:          Try
  14:                 ofs = New FileStream(_filePath, FileMode.Append, _
  15:                   FileAccess.Write, FileShare.None)
  16:                  Dim oFile As New StreamWriter(ofs)
  18:                  Dim sLine As String = sMsg & ControlChars.NewLine
  20:                  oFile.Close()
  21:                  ofs.Close()
  22:          Catch e As Exception
  23:              Throw
  24:          End Try
  25:          Finally
  26:          'Release the mutex to allow others to call
  27:              _fileMutex.ReleaseMutex()
  28:          End Try
  29:      End Sub
  30:  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.


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!

tags: Tutorials, .NET 2.0, .NET 3.5, C#, VB
comments powered by Disqus

Content provided in this blog is provided "AS-IS" and the information should be used at your own discretion.  The thoughts and opinions expressed are the personal thoughts of Mitchel Sellers and do not reflect the opinions of his employer.

Content Copyright

Content in this blog is copyright protected.  Re-publishing on other websites is allowed as long as proper credit and backlink to the article is provided.  Any other re-publishing or distribution of this content is prohibited without written permission from Mitchel Sellers.