如何在C#中使进程(而不是线程)同步文件系统访问

时间:2015-05-13 04:22:34

标签: c# windows multithreading

今天早些时候我正在调试有点像这样的东西:

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");
    }

}

让我们甚至不知道这是多么可怕......但它本质上是试图使用一个名为lock.txt的文件作为其互斥体。该操作不是原子操作,如果另一个进程正在使用它,其他进程只是没有通过关键部分(如果您可以相信它们,它们的目的是能够在锁定释放后继续)等等。显然它需要修复。

如何正确获取锁以跨多个进程同步对文件系统的访问?这些进程是同一进程的多个实例,因此它们可以共享一些协议而不是专门锁定目录(即,它们可以轻松地使用与private final static Object lock = new Object();之类的某些类锁定的所有实例等效的同步访问静态方法)

1 个答案:

答案 0 :(得分:8)

您应该使用Mutex

  

同步原语,也可用于进程间同步

互斥锁是本地到进程或名为。要支持进程间同步,您需要使用名为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);
            }
        }            
    }
}

示例程序输出:

mutex_test

紫色线条表示在两个进程之间传输共享互斥锁控制的点。

要同步对单个文件的访问,您应该使用基于受保护文件的规范化路径的互斥锁名称。

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();
}