8

是否有跨进程工作的读/写锁定机制(类似于互斥锁,但读/写而不是排他锁定)?我想允许并发读取访问,但独占写入访问。

4

6 回答 6

5

Windows 不包括跨进程读写器锁。可以使用 Semaphore 和 Mutex 的组合来构造它们(Mutex 由 writer 持有以进行独占访问,或者由 Reader 持有,然后使用 Semaphore 释放其他 reader——即 writer 将只等待 mutex 和 reader 中的任何一个) .

但是,如果预计争用较低(即没有线程长时间持有锁),那么互斥可能仍然更快:读写锁的额外复杂性超过了允许多个读者进入的任何好处。(一个读写器只有当有更多的读者并且锁被持有很长时间时,锁才会更快——但只有你的分析可以证实这一点。)

于 2010-08-17T15:55:06.200 回答
5

不。正如 Richard 上面提到的,.NET 中没有这种开箱即用的机制。这是如何使用互斥锁和信号量来实现它。

方法 #1 在http://www.joecheng.com/blog/entries/Writinganinter-processRea.html中有描述,引用:

// create or open global mutex
GlobalMutex mutex = new GlobalMutex("IdOfProtectedResource.Mutex");
// create or open global semaphore
int MoreThanMaxNumberOfReadersEver = 100;

GlobalSemaphore semaphore = new GlobalSemaphore("IdOfProtectedResource.Semaphore", MoreThanMaxNumberOfReadersEver);

public void AcquireReadLock()
{
  mutex.Acquire();
  semaphore.Acquire();
  mutex.Release();
}

public void ReleaseReadLock()
{
  semaphore.Release();
}

public void AcquireWriteLock()
{
  mutex.Acquire();
  for (int i = 0; i < MoreThanMaxNumberOfReadersEver; i++)
    semaphore.Acquire(); // drain out all readers-in-progress
  mutex.Release();
}

public void ReleaseWriteLock()
{
  for (int i = 0; i < MoreThanMaxNumberOfReadersEver; i++)
    semaphore.Release();
}

另一种选择是:

读取锁定 - 如上所述。写锁定如下(伪代码):

- Lock mutex
- Busy loop until the samaphore is not taken AT ALL:
-- wait, release.
-- Release returns value; 
-- if value N-1 then break loop.
-- yield (give up CPU cycle) by using Sleep(1) or alternative
- Do write
- Release mutex

必须注意,更有效的方法是可能的,如下所示:http ://en.wikipedia.org/wiki/Readers-writers_problem#The_second_readers-writers_problem 在上面的文章中查找“此解决方案次优”的字样。

于 2013-02-26T15:56:07.817 回答
2

我根据 Pavel 的回答创建了这个类。我还没有对它进行广泛的测试,但是我创建了一个简单的 winforms 应用程序来测试它,到目前为止它运行良好。

请注意,它使用信号量,因此不支持重入。

public class CrossProcessReaderWriterLock
{
    private readonly string _name;
    const int _maxReaders = 10;

    readonly Mutex     _mutex;
    readonly Semaphore _semaphore;

    public CrossProcessReaderWriterLock(string name)
    {
        _name = name;
        _mutex     = new Mutex(false, name + ".Mutex");
        _semaphore = new Semaphore(_maxReaders, _maxReaders, name + ".Semaphore");
    }

    public void AcquireReaderLock()
    {
        //Log.Info($"{_name} acquiring reader lock...");

        _mutex    .WaitOne();
        _semaphore.WaitOne();
        _mutex    .ReleaseMutex();

        //Log.Info($"{_name} reader lock acquired.");
    }

    public void ReleaseReaderLock()
    {
        _semaphore.Release();

        //Log.Info($"{_name} reader lock released.");
    }

    public void AcquireWriterLock()
    {
        //Log.Info($"{_name} acquiring writer lock...");

        _mutex.WaitOne();

        for (int i = 0; i < _maxReaders; i++)
            _semaphore.WaitOne(); // drain out all readers-in-progress

        _mutex.ReleaseMutex();

        //Log.Info($"{_name} writer lock acquired.");
    }

    public void ReleaseWriterLock()
    {
        for (int i = 0; i < _maxReaders; i++)
            _semaphore.Release();

        //Log.Info($"{_name} writer lock released.");
    }
}
于 2018-12-14T22:05:19.910 回答
0

如果您想避免Writer 饥饿,那么您可以考虑另一种算法。我研究了一些算法,这些算法可以避免 Writer 问题的饥饿(例如在本文中)。解决方案建议伪代码之一如下:伪代码图像

public class ReadWriterSynchronizer : IDisposable
{
    public ReadWriterSynchronizer(string name, int maxReaderCount)
    {
        myIncomingOperation = new Semaphore(1, 1, name + ".Incoming");
        myReadOperation = new Semaphore(1, 1, name + ".Reader");
        myWriteOperation = new Semaphore(1, 1, name + ".Writer");
        myCrossprocessCounter = new ReaderCounter(name + ".Counter", maxReaderCount);
    }

    public void EnterReadLock()
    {
        myIncomingOperation.WaitOne();
        myReadOperation.WaitOne();

        // Local variable is necessary, because of optimalization
        int currentCount = myCrossprocessCounter.Increase();
        if (currentCount == 1)
        {
            myWriteOperation.WaitOne();
        }

        myReadOperation.Release();
        myIncomingOperation.Release();
    }

    public void ExitReadLock()
    {
        myReadOperation.WaitOne();

        // Local variable is necessary, because of optimalization
        int currentCount = myCrossprocessCounter.Decrease();
        if (currentCount == 0)
        {
            myWriteOperation.Release();
        }

        myReadOperation.Release();
    }

    public void EnterWriteLock()
    {
        myIncomingOperation.WaitOne();
        myWriteOperation.WaitOne();
    }

    public void ExitWriteLock()
    {
        myWriteOperation.Release();
        myIncomingOperation.Release();
    }

    public void Dispose()
    {
        myIncomingOperation?.Dispose();
        myReadOperation?.Dispose();
        myWriteOperation?.Dispose();
        myCrossprocessCounter?.Dispose();

        GC.SuppressFinalize(this);
    }

    private readonly ReaderCounter myCrossprocessCounter;
    private readonly Semaphore myIncomingOperation;
    private readonly Semaphore myReadOperation;
    private readonly Semaphore myWriteOperation;
}

不幸的是,ctr变量是一个整数,因此它只能在进程间场景中工作。我决定用信号量计数器( ReaderCounter) 替换整数计数器,这样它就可以用于跨进程通信。本质上,我WaitOne(0)是为了减少Release()增加读者计数器。

internal class ReaderCounter : IDisposable
{
    internal ReaderCounter(string name, int maxConcurrentRead)
    {
        MaximumCount = maxConcurrentRead + InitialCount;
        myReadCounterSemaphore = new Semaphore(InitialCount, MaximumCount, name);
        myIncomingOperation = new Semaphore(1, 1, name + ".Incoming");
    }

    internal int Increase()
    {
        int counter = RetrieveCurrentCount();

        // Not allowing to exceed maximum count
        if (counter != MaximumCount - 1)
        {
            counter = myReadCounterSemaphore.Release();
        }
        else
        {
            counter++;
        }

        return counter;
    }

    internal int Decrease()
    {
        int counter = RetrieveCurrentCount() - 1;
        myReadCounterSemaphore.WaitOne(0);

        return counter;
    }

    public void Dispose()
    {
        myReadCounterSemaphore?.Dispose();
        myIncomingOperation?.Dispose();

        GC.SuppressFinalize(this);
    }

    internal int MaximumCount { get; private set; }

    private const int InitialCount = 1;
    private readonly Semaphore myReadCounterSemaphore;
    private readonly Semaphore myIncomingOperation;

    private int RetrieveCurrentCount()
    {
        myReadCounterSemaphore.WaitOne(0);
        int counter = myReadCounterSemaphore.Release();
        return counter;
    }
}

注:为方便使用,阅读器计数器增加了 1 个河豚计数。例如,使用 5 个阅读器意味着 [1,6] 初始信号量计数。从最小计数减少返回 -1,从最大计数增加返回最大计数 +1。

更新:我创建了一个带有控制台应用程序的 GitHub 存储库,因此您可以使用它。它还包含 ReaderWriterSynchronizerTryEnterReadLock()TryEnterWriteLock()方法:https ://github.com/SzilvasiPeter/Cross-process-ReaderWriterLock

于 2021-08-04T13:16:13.473 回答
-1

System.Threading.Mutex 有一个互斥锁,可用于进程内通信。如果您想要它不支持的功能,可以通过互斥锁来实现。

于 2010-08-17T15:05:17.867 回答
-4

你看过System.Threading.ReaderWriteLock吗?这是MSDN链接。

于 2010-08-17T15:05:21.003 回答