是否存在跨进程工作的读/写锁定机制(类似于Mutex,但是读/写而不是独占锁定)?我想允许并发读访问,但是允许独占写访问。
答案 0 :(得分:5)
Windows不包含跨进程Reader-Writer锁。可以使用Semaphore和Mutex的组合来构造一个(Mutex由作者独立访问或由读者持有,然后读者使用Semaphore释放其他读者 - 即编写者将等待互斥和读者)
但是,如果预计争用率很低(即没有线程长时间持有锁),那么互斥可能仍然会更快:读写器锁的额外复杂性压倒了允许多个读者进入的任何好处。(A如果有更多的读者和锁定持续很长时间,读写器锁只会更快 - 但只有你的分析可以确认这一点。)
答案 1 :(得分:5)
没有。正如理查德在上面提到的那样,.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 在上面的文章中查找“此解决方案不是最理想”的字样。
答案 2 :(得分:0)
我已经根据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.");
}
}
答案 3 :(得分:0)
如果您想避免作家饥饿,那么您可以考虑另一种算法。我研究了一些算法,可以避免 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
答案 4 :(得分:-1)
System.Threading.Mutex有一个可用于进程内通信的互斥锁。如果您想要它不支持的功能,可以通过互斥锁实现。
答案 5 :(得分:-2)
你看过System.Threading.ReaderWriteLock
了吗?这是MSDN链接。