1
votes

I came across an issue, and I'm not sure if it's me or if there's an issue with thread locking.

I have a class I use for basic utilities. In that class is method to create or append a text file. And because I use it debug, I have the method using lock() to keep the access singular. Except, it appears to be failing and allowing multiple threads into the blocked code.

When running my test threads it doesn't throw an error every time. It's a little weird. There are 50 threads/tasks being created. Each thread is writing a line to a singe file using the class below. It cycles through about 3100 individual tasks. But a maximum of 50 tasks are created to handle each batch. As each thread completes its task, a new one is created to take its place. The last batch processed 3188 commands and threw 16 errors.

I have tried using Monitor.Enter and Exit, but I get the same results. I have also tried making the StdLibLockObj readonly. All with the same results.

Error: The process cannot access the file 'ThreadExe.txt' because it is being used by another process.

   static class StdLib
   {
      private static object StdLibLockObj = new object();
      public static void WriteLogFile(string @AFileName, string FileData, bool AppendIfExists = true, bool AddAppPath = true)
      {
         lock (StdLibLockObj)
         {
            StreamWriter sw = null;
            try
            {
               if (AddAppPath)
               {
                  AFileName = @Path.Combine(@ApplicationPath(), @AFileName);
               }

               if ((AppendIfExists) && File.Exists(AFileName))
               {
                  sw = File.AppendText(AFileName);
               }
               else
               {
                  sw = File.CreateText(AFileName);
               }
               sw.Write(FileData);
            }
            finally
            {
               if (sw != null)
               {
                  sw.Flush();
                  sw.Close();
                  sw.Dispose();
               }
               sw = null;
            }
         }
      }
  }

My background is mostly in Delphi, where threading is a bit more granular.

Any help would be appreciated.

1
Your issue is not with locking. Look at your error message; it says "...it is being used by another process". So, the question you need to find the answer for is who is using this file (i.e., who has a file handle open for that file) while your code is trying to write to it... (by the way, you do not need to do sw.Flush() and sw.Close() explicitely, since sw.Dispose() will take care of this..) - user2819245
is would also wrap the StreamWriter object around a using this way you will not have to utilize the finalize section because the using(){} will auto dispose the object. after appending call flush() immediately as well - MethodMan
(FYI: Even though the error message mentions 'another process', it could also be your program which has another handle to this file open. If your code touches/accesses log files in methods/places other than WriteLogFile, check if they are also synchronizing access using the same lock object StdLibLockObj...) - user2819245
The file is only accessed using the WriteLogFile() method. And in this scenario, it is only called from a new task/thread. It looks like the lock() method is not blocking other threads, or the Close(), Dispose() is not executing in a timely manner. So the next thread gets in to access the file and previous thread's commands have not been fully executed. - S. Zimm
Well, I feel a bit embarrassed...@elgonzo was correct. There was another process. It was the OS. I had just deleted the file before running the process. Apparently it takes Windows a couple of seconds to release the handle after the delete. I didn't make the correlation until this morning and couple cups of strong coffee. I really appreciate everyone who took the time to help. My apologies. - S. Zimm

1 Answers

1
votes

Wrap your StreamWriter entries in a "using" block. That will get rid of locking. Sort of like this:

    public static void ErrorMessage(string logMessage)
    {
        using (StreamWriter sw_errors = new StreamWriter(m_errors, true))  
        {

            sw_errors.Write("\r\nLog Entry : ");
            sw_errors.WriteLine("{0} {1}", DateTime.Now.ToLongTimeString(),
                DateTime.Now.ToLongDateString());
            sw_errors.WriteLine("  :");
            sw_errors.WriteLine("  :{0}", logMessage);
            sw_errors.WriteLine("-------------------------------");

        }
    }