Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to have processes (not threads) in C# synchronize file system access

Earlier today I was debugging something that went a bit like this:

class Foo {

    void AccessCriticalSection() {
        try {
            if (IO.File.Exists("\\path\to\lock.txt")
                throw new Exception();
            System.IO.File.Create("\\path\to\lock.txt");
            criticalSection();          
        } catch (Exception ex) {
            // ignored
        } 
    }

    void CriticalSection() {
        // banana banana banana
        System.IO.File.Delete("\\path\to\lock.txt");
    }

}

Let's not even get into how terrible this is… but it's essentially trying to use a file called lock.txt as its mutex. The operation isn't atomic, other processes are just not getting through the critical section if another process is using it (they're intended to be able to continue after the lock is released if you can believe it), etc. etc. Obviously it needs to be fixed.

How do I properly obtain a lock to synchronize access to the filesystem across multiple processes? The processes are multiple instances of the same process, so they can share some protocol rather than specifically locking the directory (i.e., they can easily use what is the equivalent to all instances of some class locking on something like private final static Object lock = new Object(); to synchronize access to static methods)

like image 316
Joseph Nields Avatar asked May 13 '15 04:05

Joseph Nields


People also ask

Can a process exist without thread?

It is NOT possible to do a single task (not multiple tasks) using only a process without thread.

Does each process have its own thread?

Each process is started with a single thread, often called the primary thread, but can create additional threads from any of its threads. A thread is the entity within a process that can be scheduled for execution. All threads of a process share its virtual address space and system resources.

What is not thread safe in C?

These extended mathlib functions use a global variable, _signgam , so are not thread-safe. The C89 multibyte conversion functions (defined in stdlib. h ) are not thread-safe, for example mblen() and mbtowc() , because they contain internal static state that is shared between all threads without locking.

What part of a process is not shared by threads?

Threads share the same memory, processes do not.


1 Answers

You should use a Mutex.

A synchronization primitive that can also be used for interprocess synchronization.

Mutexes are either local to a process or named. To support interprocess synchronization you'll need to use a named mutex.

Named system mutexes are visible throughout the operating system, and can be used to synchronize the activities of processes.


Here's a sample program that demonstrate interprocess synchronization using a shared mutex.

class Program
{
    static void Main(string[] args)
    {
        const string SHARED_MUTEX_NAME = "something";
        int pid = Process.GetCurrentProcess().Id;

        using (Mutex mtx = new Mutex(false, SHARED_MUTEX_NAME))
        {
            while (true)
            {
                Console.WriteLine("Press any key to let process {0} acquire the shared mutex.", pid);
                Console.ReadKey();

                while (!mtx.WaitOne(1000))
                {
                    Console.WriteLine("Process {0} is waiting for the shared mutex...", pid);
                }

                Console.WriteLine("Process {0} has acquired the shared mutex. Press any key to release it.", pid);
                Console.ReadKey();

                mtx.ReleaseMutex();
                Console.WriteLine("Process {0} released the shared mutex.", pid);
            }
        }            
    }
}

Sample program output:

mutex_test

The purple lines show points where control of the shared mutex is transferred between the two processes.


To synchronize access to individual files you should use a mutex name that is based on a normalized path of the protected file.

Here's how you can create a normalized path:

public static string NormalizePath(string path)
{
    return Path.GetFullPath(new Uri(path).LocalPath)
               .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
               .ToUpperInvariant();
}
like image 89
Mårten Wikström Avatar answered Oct 10 '22 16:10

Mårten Wikström