c# inter process acquire/release 资源计数

c# inter process acquire/release resource count

我有一个资源可能在许多进程之间共享,并且在不再使用时需要清理。

在 C# 中维护跨进程使用计数以便在不再使用时清理资源的最简单方法是什么?

(如果使用资源的最后一个进程死亡,只要下一个使用的进程具有有效的使用计数,就可以不释放资源)。

如果所有进程都在同一台机器上,我会创建一个包含实例计数的文件。当进程提供资源时,应该打开文件进行写入,从而有效地锁定文件。应该读取并递增实例计数,然后释放文件上的写锁。

当一个进程退出(或完成对资源的使用)时,获取文件的写锁并减少资源计数。如果资源计数在减少后为零,则启动您的清理过程。

您将必须具有重试逻辑,以防第二个进程试图获取共享文件上的写锁,而该文件已打开以供另一个进程写入。

重要的一点是 OS 可以给你一个独占写锁,这实际上是你的临界区。

您描述的是在 .net 远程处理中实现的模式,其中资源的使用者可能在不同的机器上。这是通过强制消费者显式释放对象来实现的,并以终身租约为后盾,如果他们没有刷新租约,消费者会自动超时。

鉴于所有这些都已经在 .net 框架中,我建议使用远程处理来生成您的框架。

我使用的代码:

internal sealed class InterProcessResource {
    private static readonly string MutexNameThisProcess = "IPR-" + Guid.NewGuid().ToString();
    private static readonly Mutex MutexThisProcess = new Mutex(true, MutexNameThisProcess);

    private readonly MemoryMappedFile mmf;
    private readonly string mutexName;

    public InterProcessResource(string resourceName) {
        this.mutexName = resourceName + "-mtx";
        this.mmf = MemoryMappedFile.CreateOrOpen(resourceName + "-mmf", 16 * 1024, MemoryMappedFileAccess.ReadWrite);
    }


    public void Acquire(Action initAction) {
        using (new Mutex(true, this.mutexName)) {
            var currentList = ReadStringList(mmf);
            if (currentList.Count == 0) {
                initAction();
            }
            var newList = PruneMutexList(currentList);
            newList.Add(MutexNameThisProcess);
            WriteStringList(this.mmf, newList);
        }
    }

    public void Release(Action freeAction) {
        using (new Mutex(true, this.mutexName)) {
            var currentList = ReadStringList(this.mmf);
            var newList = PruneMutexList(currentList);
            WriteStringList(this.mmf, newList);
            if (newList.Count == 0) {
                freeAction();
            }
        }
    }

    private static List<string> ReadStringList(MemoryMappedFile mmf) {
        var list = new List<string>();
        using (var stream = mmf.CreateViewStream()) {
            var reader = new BinaryReader(stream);
            int count = reader.ReadInt32();
            for (int i = 0; i < count; i++) {
                list.Add(reader.ReadString());
            }
        }
        return list;
    }

    private static void WriteStringList(MemoryMappedFile mmf, List<string> newList) {
        using (var stream = mmf.CreateViewStream()) {
            var writer = new BinaryWriter(stream);
            int count = newList.Count;
            writer.Write(count);
            for (int i = 0; i < count; i++) {
                writer.Write(newList[i]);
            }
        }
    }

    // removes our mutex name AND any dead processes mutex names
    private static List<string> PruneMutexList(List<string> list) {
        var newList = new List<string>();
        foreach (var s in list) {
            if (s != MutexNameThisProcess) {
                Mutex m;
                if (Mutex.TryOpenExisting(s, out m)) {
                    newList.Add(s);
                    m.Dispose();
                }
            }
        }
        return newList;
    }
}