如果您想避免 Writer starvation,那么您可以考虑另一种算法。我研究了一些算法,这些算法可以避免 Writer 问题的饥饿(例如,在这个 paper 中)。解决方案提案伪代码之一如下:pseudo-code image。
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 存储库,因此您可以使用它。它还包含带有TryEnterReadLock() 和TryEnterWriteLock() 方法的ReaderWriterSynchronizer:https://github.com/SzilvasiPeter/Cross-process-ReaderWriterLock