我正在使用 C++ boost::thread 库,就我而言,这意味着我正在使用 pthreads。正式地,必须从锁定它的同一个线程解锁互斥锁,我想要能够锁定一个线程然后解锁另一个线程的效果。有很多方法可以做到这一点。一种可能性是编写一个允许这种行为的新互斥类。
例如:
class inter_thread_mutex{
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.
}
我应该指出,上面的代码并不能保证 FIFO 访问,因为如果一个线程调用 lock() 而另一个调用 unlock(),则第一个线程可能会先于其他正在等待的线程获取锁。(想想看, boost::thread 文档似乎没有为互斥锁或条件变量做出任何明确的调度保证)。但是,让我们暂时忽略这一点(以及任何其他错误)。
我的问题是,如果我决定走这条路,我是否能够使用这样的互斥锁作为 boost Lockable 概念的模型。例如,如果我使用 boost::unique_lock< inter_thread_mutex >
for RAII 样式访问,然后将此锁传递给boost::condition_variable_any.wait()
等,会不会出现任何问题?
一方面,我不明白为什么不这样做。另一方面,“我不明白为什么不”通常是确定某事是否可行的一种非常糟糕的方式。
我问的原因是,如果事实证明我必须为 RAII 锁和条件变量以及其他任何东西编写包装类,那么我宁愿找到其他方法来达到相同的效果。
编辑:我想要的那种行为基本上如下。我有一个对象,每当修改它时都需要锁定它。我想从一个线程锁定对象,并对其进行一些工作。然后我想在告诉另一个工作线程完成工作时保持对象锁定。因此,当工作线程完成时,第一个线程可以继续执行其他操作。当工作线程完成时,它会解锁互斥锁。
而且我希望过渡是无缝的,所以当线程 1 开始工作和线程 2 完成它时,没有其他人可以在两者之间获得互斥锁。
像 inter_thread_mutex 这样的东西似乎可以工作,并且它还允许程序与其交互,就好像它是一个普通的互斥锁一样。所以这似乎是一个干净的解决方案。如果有更好的解决方案,我也很乐意听到。
再次编辑:我需要锁开始的原因是有多个主线程,并且锁在那里防止它们以无效的方式同时访问共享对象。所以代码已经在主线程级别使用了循环级别的无锁操作序列。此外,在最初的实现中,没有工作线程,互斥锁是普通的 Kosher 互斥锁。
inter_thread_thingy 是作为优化提出的,主要是为了提高响应时间。在许多情况下,保证操作 A 的“第一部分”发生在操作 B 的“第一部分”之前就足够了。举个愚蠢的例子,假设我打了对象 1 并给它一个黑眼圈。然后我告诉对象 1 改变它的内部结构以反映所有的组织损伤。在继续打对象 2 之前,我不想等待组织损伤。但是,我确实希望组织损伤作为同一操作的一部分发生;例如,在此期间,我不希望任何其他线程以使组织损坏无效操作的方式重新配置对象。(是的,这个例子在很多方面都不完美,不,我不是在做游戏)
因此,我们对模型进行了更改,其中可以将对象的所有权传递给工作线程以完成操作,并且它实际上工作得很好;每个主线程都能够完成更多操作,因为它不需要等待它们全部完成。而且,由于主线程级别的事件排序仍然是基于循环的,因此编写高级主线程操作很容易,因为它们可以基于操作完成的假设(更准确地说,关键的“当相应的函数调用返回时,排序逻辑所依赖的第一部分完成)。
最后,我认为使用带有增强锁的 RAII 使用 inter_thread mutex/semaphore thingies 来封装使整个事情正常工作所需的必要同步会很好。