我正在尝试找到(或实现)支持低优先级写入器的读取器/写入器锁,但在研究任何现有解决方案时均未成功。
我所说的低优先级作家的意思是:“将让位”给新来的读者或普通作家。
如果有源源不断的读取器肯定会导致饥饿,但这可以通过定时锁变体(“尝试定时低优先级写入器锁”,然后在超时时切换到普通锁)或通过改变读取器的方式来解决发出(可能会定期停止读取一个短窗口)。
如果有任何文献描述了这些内容,我还没有找到。
如果有利用常规锁的已知(正确!)解决方案,我将不胜感激。
我正在尝试找到(或实现)支持低优先级写入器的读取器/写入器锁,但在研究任何现有解决方案时均未成功。
我所说的低优先级作家的意思是:“将让位”给新来的读者或普通作家。
如果有源源不断的读取器肯定会导致饥饿,但这可以通过定时锁变体(“尝试定时低优先级写入器锁”,然后在超时时切换到普通锁)或通过改变读取器的方式来解决发出(可能会定期停止读取一个短窗口)。
如果有任何文献描述了这些内容,我还没有找到。
如果有利用常规锁的已知(正确!)解决方案,我将不胜感激。
我不知道 100% 喜欢你的提议,但是有一些现有的接口很接近:
许多现有的读/写锁定 API 都有一个“尝试锁定”接口,就像pthread_rwlock_trywrlock()
在 UN*X 系统上一样。这些是无等待的,只有在没有人拥有它也没有等待它的情况下才会获取锁。
您通常会使用这种旋转来锁定,和/或人为地延迟尝试代码(回退)。即有如下代码:
for (count = 0; count < MAX_SPINS && (locked = trywrlock(lock)); count++);
if (locked) {
delay(some_backoff);
wrlock(lock); /* or try spinning loop above again ? */
}
不幸的是,这并不是您所要求的。它会延迟锁定,但是低优先级写入器将在 CPU 上旋转和/或由于(可能不必要的)退避而以延迟的方式获取它。
Solaris 内核有一个接口rw_tryupgrade(9f)
,可用于测试当前线程是否是锁上的唯一读取器,没有写入器等待,如果是,则将锁升级为独占/写入,即您的代码如下:
if (!rw_tryenter(lock, RW_WRITER)) {
rw_enter(lock, RW_READER); /* this might wait for a writer */
if (!rw_tryupgrade(lock)) { /* this fails if >1 reader / writer waiting */
/* do what you must to if you couldn't get in immediately */
}
}
这有点接近您的要求,但仍然不完全相同 - 如果它失败,您必须放弃 readlock,可能退出(等待),重新获取 readlock,尝试升级。该过程再次相当于旋转。
此外,许多 UNIX 系统实际上至少按照调度优先级的顺序执行等待者唤醒。wrlock()
因此,在尝试普通的等待调用之前,您必须使线程的优先级最低(如果必要的话,人为地) ;根据调度程序的工作方式,在您的线程等待时想要相同的写锁的其他人将在此之前获得它。虽然在不一定保证的多处理器/核心系统上......
最后,SymbianOS(Symbian^3 版本)有一个RRWlock
类可以让读者优先于写者,这样如果有读者等待/进来,它就会故意让写者饿死。同样,不完全是你想要的行为,因为它影响给定锁上的所有作者,而不仅仅是特定的。
恐怕您必须编写自己的优先级锁,并带有两个写入器唤醒队列。
在这里,您正在查看的是读者和作者之间的优先级,这样低优先级的作者总是给高优先级的读者第一次机会。由于它的慷慨,这显然会导致低优先级写入者的饥饿。
这可以在两个不同的层面上实现: 1. 从应用程序方面:这是一种通常的方法,因为通常互斥体没有偏见。在线程争夺锁定之前,应用程序逻辑应该自己决定哪个线程具有更高的优先级并允许该线程去锁定。这通常成为特定于应用程序的:
--> task executioner 方法:executioner 线程仅根据优先级执行可用任务。它解决了执行者级别的基于优先级的执行,但同样的问题在该级别之上弹出。这可以作为基于 FIFO 的长互斥锁实现。这也需要解决饥饿问题。
在我的脑海中,你会想要这样的东西:
class PriorityRWLock
{
int rwcount;
mutex mtx;
condition_variable cv;
priority_queue<writer_info> writers;
};
... 和 PriorityRWLock::acquire_write_lock() 看起来像:
lock_guard raii(mtx);
do {
if ( rwcount==0 ) // == no read or write locks
{
if ( writers.top().thread == thread::self() )
{ rwcount = -1; writers.pop_front(); break; } // == exclusive write access
else
{
// wake up the waiting thread(s) and sleep
writers.push( writer_info(thread::self(),priority) );
cv.notify_all();
cv.wait(mtx);
}
}
else
{ cv.wait(mtx); } // sleep
} while ( true );
...或类似的东西。
它不会过于高效。您确实更愿意将 rwcount 存储在 atomic_int 或类似的文件中,但对 condition_variable 的需求排除了这一点。
由于可能需要间歇地等待(),因此定时锁定会很棘手。try_write_lock() 应该是可行的。