【发布时间】:2010-05-03 01:21:55
【问题描述】:
假设我编写了一个 C++ 信号量类,其接口模拟了 boost Lockable 概念(即lock(); unlock(); try_lock(); 等)。对此类对象的 RAII 访问使用增强锁是否安全/推荐?换句话说,boost 锁(和/或 boost 线程库的其他相关部分)是否假设 Lockable 概念将仅由从同一线程锁定和解锁的类似互斥体的对象建模?
我的猜测是使用信号量作为 Lockable 的模型应该没问题。我浏览了一些增强源,它“似乎”还可以。这些锁似乎没有存储对 this_thread 或类似内容的显式引用。此外,Lockable 概念没有whichThreadOwnsMe() 之类的任何功能。看起来我什至应该能够将boost::unique_lock<MySemaphore> 引用传递给boost::condition_variable_any::wait。但是,文档并没有明确说明要求。
为了说明我的意思,考虑一个基本的二进制信号量类:
class MySemaphore{
bool locked;
boost::mutex mx;
boost::condition_variable cv;
public:
void lock(){
boost::unique_lock<boost::mutex> lck(mx);
while(locked) cv.wait(lck);
locked=true;
}
void unlock(){
{
boost::lock_guard<boost::mutex> lck(mx);
if(!locked) error();
locked=false;
}
cv.notify_one();
}
// bool try_lock(); void error(); etc.
}
现在假设某个地方,无论是在对象上还是在全局范围内,我都有
MySemaphore sem;
我想使用 RAII 锁定和解锁它。此外,我希望能够将锁的所有权从一个线程“传递”到另一个线程。例如,我在一个线程中执行
void doTask()
{
boost::unique_lock<MySemaphore> lock(sem);
doSomeWorkWithSharedObject();
signalToSecondThread();
waitForSignalAck();
lock.release();
}
当另一个线程正在执行类似的操作时
{
waitForSignalFromFirstThread();
ackSignal();
boost::unique_lock<MySemaphore>(sem,boost::adopt_lock_t());
doMoreWorkWithSameSharedObject();
}
我这样做的原因是我不希望其他人能够在第一个线程执行doSomeWorkWithSharedObject() 和第二个线程执行doMoreWorkWithSameSharedObject() 之间获得锁定sem .基本上,我将一项任务分为两部分。我将任务拆分的原因是(1)我希望任务的第一部分尽快开始,(2)我想保证第一部分在 doTask() 返回之前完成, (3) 我希望任务的第二个更耗时的部分由另一个线程完成,可能是从等待完成由主线程启动的任务的从属线程池中选择的。
注意:我最近在这里Modelling boost::Lockable with semaphore rather than mutex (previously titled: Unlocking a mutex from a different thread) 发布了同样的问题(有点) 但是我把互斥锁和信号量混淆了,所以关于使用升压锁的问题并没有真正得到解决。
【问题讨论】:
-
您可以更改其他问题的标题。我认为这比重新发布要好。
-
Lockable 的
lock函数的后置条件和unlock函数的前置条件是该线程“拥有”锁。因此,尽管没有返回拥有线程的函数,但 Lockable 是在这些术语中定义的。现在,没有什么可说的 Lockable 对象不能有另一个函数来原子地将锁的所有权从一个线程转移到另一个线程,所以我不认为你违反了 Lockable 的合同来做到这一点。实际上实现所有权的原子转移会很棘手,这取决于您希望它有多可靠。 -
... 另外,如果您使用
unique_lock并在该对象的生命周期内转移锁的所有权,那么您将处于与使用 @987654336 相同的罪恶状态@ 并在生命周期内调用unlock。你在搞乱unique_lock的合同,即 它 管理 Lockable 的所有权,而你让它去做。 -
@Steve:在我看来,既然 Lockable 永远不会被问到哪个线程拥有它,那么我不需要实际转移所有权,只要我保证每次调用
lock紧随其后的是对unlock的一次调用(来自任何线程)。或者,换一种说法,每当任何线程调用unlock时,我都可以隐式地将所有权转移给解锁线程,而无需实际执行任何操作。在我看来,这种行为与实际转让所有权没有区别。 -
@steve:这个问题的要点是“当前线程”引用是否有意义。信号量不属于特定线程。它通常模拟传递一个不可见的令牌(或多个令牌)。传统上,这些令牌根本不是由对象建模的,因此很不清楚 Dan 是否需要实现任何东西的转移。
标签: c++ semaphore boost-thread