Click here to Skip to main content
15,881,413 members
Please Sign up or sign in to vote.
5.00/5 (2 votes)
See more:
See approved solution below - because the file is being accessed from two different user spaces, the Mutex needs to be declared as Global. Thanks to Andreas for the solution!

=========================
OK, I have a log file that can be written to by multiple processes. I'm trying to protect it with a named Mutex, but I'm still occasionally getting "file is in use by another process" errors when trying to open the file and I can't understand why. As far as I can tell, I'm using the Mutex correctly. Any pointers would be greatly appreciated...

Relevant (simplified) code is below - this is in an assembly that is called by both processes, and I have verified that both processes believe that they are using the same Mutex by having one process create the Mutex and the other use OpenExisting.

C#
public class Log
{
  public static Mutex m_Mutex = new Mutex( false, "MyMutex" );

  public static void LogStuff( string stuff )
  {
    StreamWriter w = null;

    try
    {
      m_Mutex.WaitOne();

      w = File.AppendText( @"C:\temp.log" );
      w.WriteLine( stuff );
    }
    finally
    {
      if ( w != null )
      {
        w.Flush();
        w.Close();
        w = null;
      }

      m_Mutex.ReleaseMutex();
    }
  }
}


Other (possibly) important notes:
- This is currently happening in Debug mode from VS2008, where I am debugging only the main process.

- There are exactly 2 processes that can access the file, both of which are running on the same machine. The need for 2 processes has to do with some 3rd-party drivers that require Admin privileges, while we want the main application to run as the current user, even if they don't have Admin rights.
Posted
Updated 27-Mar-13 2:39am
v3
Comments
Paulo Zemek 26-Mar-13 15:44pm    
Are you accessing the file from many processes in the same computer? Or are you acessing the file from the network?
tzych 26-Mar-13 16:04pm    
Paulo - the file is accessed from exactly 2 processes on the same computer.
Zoltán Zörgő 26-Mar-13 15:58pm    
Debugging might mislead you in a multithreaded situation. As I see, it should work. Even NLog is using the same approach: http://svn.nlog-project.org/repos/nlog/trunk/src/NLog/Internal/FileAppenders/MutexMultiProcessFileAppender.cs
Paulo Zemek 26-Mar-13 16:29pm    
Well... so, if there is no one else reading the file... I simple don't know... the code seems fine to me.
CHill60 26-Mar-13 17:08pm    
I may be way off base here but is this assembly a singleton?

1 solution

Change your code as follows:
1) Give the Mutex the name @"Global\MyMutex".
2) Call Dispose() on w after (or instead of) Close().
Do the same for your other process.

[EDIT]
I would simplify the code as follows:
C#
public class Log
{
  public static Mutex m_Mutex = new Mutex( false, @"Global\MyMutex" );
 
  public static void LogStuff( string stuff )
  {
    try
    {
      m_Mutex.WaitOne();
      using(var w = File.AppendText( @"C:\temp.log" ))
      {
        w.WriteLine( stuff );
      }
    }
    finally
    {
      m_Mutex.ReleaseMutex();
    }
  }
}

This using block calls the Dispose at the end of the block (thus, flushing and closing the stream and releaseing the proxy object of the file on the file system).
[/EDIT]

Cheers
Andi
 
Share this answer
 
v2
Comments
SoMad 26-Mar-13 19:47pm    
I hope the OP replies to this to tell us if it solved the problem. I recall having a similar issue and coming to the conclusion that the OS had not completely released the file handle. If your Dispose solution takes care of that, it is much better than the loop I had to add, checking the status of the file before releasing the Mutex (or was that before opening the file).
I am looking through my projects, but I don't remember exactly where it was.

Soren Madsen
Andreas Gieriet 26-Mar-13 20:12pm    
Use this program and run it concurrently on two consoles by giving two differeing arguments to distinguish the log file entries:
using System.IO;
using System.Linq;
using System.Threading;

namespace GlobalMutex
{
class Program
{
public static Mutex m_Mutex = new Mutex( false, @"Global\MyMutex" );
static void Main(string[] args)
{
string inst = args.Length > 0 ? args[0] : "";
foreach (int i in Enumerable.Range(0, 1000))
{
try
{
m_Mutex.WaitOne();
using (var w = File.AppendText(@"C:\Temp\Temp.log"))
{
w.WriteLine("{0}: {1}", inst, i);
}
}
finally
{
m_Mutex.ReleaseMutex();
}
}
}
}
}

The result does not throw any exceptions.
Cheers
Andi
SoMad 26-Mar-13 20:38pm    
My code is in C++ somewhere in one of our MFC projects. As I recall, one process was receiving streaming video over a TCP connection and storing it to a file, while the other process was waiting for the file to be closed with the valid video.
In other words, many megabytes had to be flushed to the file. Your sample code probably still works with large files, but the only way of knowing for sure is to try it out.

Soren Madsen
Andreas Gieriet 27-Mar-13 16:06pm    
As Leandro stated below, the key is the Global Mutex and not the disposing. I went into reflector and saw that Close() and Dispose() can be exchanged for that particular stream class. I assume a global mutex would do for native Windows programs too, e.g. have a look at CreateMutex function (C++).
Cheers
Andi
SoMad 27-Mar-13 16:13pm    
Then it was not the same problem as I had. It just rang a bell when I read it.
In any case, the solution works and I believe I gave you 5 yesterday, but let me double-check.

Soren Madsen

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900