boost::interprocess::interprocess_condition::wait 在等待时不会原子地解锁互斥锁
Posted
技术标签:
【中文标题】boost::interprocess::interprocess_condition::wait 在等待时不会原子地解锁互斥锁【英文标题】:boost::interprocess::interprocess_condition::wait does not atomically unlock mutex while waiting 【发布时间】:2013-06-13 15:03:28 【问题描述】:正如当前帖子标题所说,boost boost::interprocess::interprocess_condition::wait 假设在等待时原子地解锁互斥锁,但事实并非如此。
在以下代码中:
boost::interprocess::scoped_lock< boost::interprocess::interprocess_mutex > state_access_lock(impl->state->state_access_mut);
impl->state->state_access_cond.wait(state_access_lock);
在 VS2010 进入调试模式时,我按下了暂停键,当我看到 state_access_lock 在等待时仍然被锁定时,我感到很惊讶。
但这不是 boost 的文档所说的 here。
有人有什么建议吗?
谢谢。
【问题讨论】:
您对代码的行为有实际的观察还是只是调试器中某个变量的值? 我首先意识到了这种行为。因为我的第二个线程应该写然后调用通知,正在等待互斥锁被永久释放。只有在那之后,我才决定使用 VS 调试模式检查我的互斥锁到第一个线程中发生了什么。 除了这种根本性的破坏不太可能被忽视之外,各种条件变量的每个单独的代码路径(顺便说一下,你应该指定你正在使用哪个)都会经历解锁互斥锁。所以错误必须在其他地方。尝试发布更多上下文。 我在 boost::interprocess::interprocess_condition 的 wait 方法中跟踪执行代码,发现互斥锁本身通过调用mut.unlock()
解锁,但 scoped_lock 本身仍然将 is_locked 设置为 true。所以我很困惑,如果我有这样的 scoped_lock:scoped_lock lk(mut)
调用 mut.unlock()
或 lk.unlock()
有什么区别?
【参考方案1】:
好的,这是第一个线程:
void CSharedMemory::start(start_mode mode)
bool start_mut_locked = true;
impl->running = true;
impl->mode = mode;
stateMetaStruct* state = impl->proc_state;
boost::interprocess::scoped_lock< boost::interprocess::interprocess_mutex > state_access_lock(state->state_access_mut);
while(impl->running)
state->data_written = false;
while(!state->data_written)
if(start_mut_locked)
// We can now unlock and let other threads to send data.
impl->start_mut.unlock();
start_mut_locked = false;
state->state_access_cond.wait(state_access_lock); // wait here upon sharedmemory's state change
boost::interprocess::offset_ptr< stateMetaStruct > s = impl->shm_obj.find< stateMetaStruct >(boost::interprocess::unique_instance).first;
state = s.get();
if(!state->data_written)
// Spurious wakeup.
glm_debug("Spurious wakeup.");
if(this == state->data_written_by_proccess)
state->data_written = false;
glm_debug("Ignoring my proper event.");
if(impl->running)
// Got action from other process.
const interprocess_actions state_action = state->action;
if(DO_STOP == state_action)
else if(DUMP_USERS_REQUEST == state_action)
impl->stateChangedListener->onDumpUsersRequest();
else if(DUMP_USERS_REPLY == state_action)
else
glm_err("Unexpected state.");
第二个线程尝试使用此方法发送数据:
void CSharedMemory::sendDumpUsersRequest()
// Ensure shm is started.
boost::mutex::scoped_lock lk(impl->start_mut);
glm_debug("%s", __FUNCTION__);
boost::interprocess::offset_ptr< stateMetaStruct > s = impl->shm_obj.find< stateMetaStruct >(boost::interprocess::unique_instance).first;
stateMetaStruct* state = s.get();
boost::interprocess::scoped_lock< boost::interprocess::interprocess_mutex > state_access_lock(state->state_access_mut);
state->action = DUMP_USERS_REQUEST;
state->data_written = true;
state->data_written_by_proccess = this;
// Send request.
state->state_access_cond.notify_all();
行为是,第二个线程在尝试获取 scoped_mutex 时阻塞,因为第一个线程正在等待它。
【讨论】:
【参考方案2】:根据目前的cmets,我想我可以推断出一个答案。
不要相信传递给 interprocess_condition::wait() 的 scoped_lock 的成员。 interprocess_condition 的合同(与 interprocess_condition_any 不同)规定您只能将其与 interprocess_mutex 的锁一起使用。知道这一点后,条件变量会将内部互斥锁从您的锁中拉出,以便比它对锁一无所知时更有效地完成工作。
因此,在解锁互斥锁时,它不会在您的 scoped_lock 上调用 unlock(),而是直接在互斥锁上调用。这对于内部实现来说很好;不要在家里这样做。如果在锁超出范围之前不重新锁定互斥锁,就会发生不好的事情。
换句话说,您在调试器中看到的行为并不表示存在问题。如果你有一个死锁,它一定是在别的地方。
编辑
给定的实际代码中的条件变量对我来说看起来不错。我发现与 start_mut 的交互有点奇怪。你确定那部分没有问题吗?
【讨论】:
好的,所以我应该写state->state_access_mut.lock(); state->state_access_cond.do_wait(state->state_access_mut);
而不是这个 boost::interprocess::scoped_lock< boost::interprocess::interprocess_mutex > state_access_lock(impl->state->state_access_mut); impl->state->state_access_cond.wait(state_access_lock);
然后操纵 state_access_lock
它自己而不是把它放入 scoped_lock
对吗?
不,编写的代码实际上看起来不错。只是条件变量直接解锁了底层的互斥锁,而不是通过锁对象,所以你在调试器中看到了令人困惑的值。但这应该是 only 效果。如果你陷入僵局,你就会遇到不同的问题。为什么不发布两个线程的代码?
我使用“添加另一个答案”发布了代码,但代码超出了您今天的答案。
我使用 start_mut 来确保第一个线程在第二个线程调用 notify_all()
之前调用 wait
。我这样做了,因为我的两个线程都是从主线程启动的,我无法知道第一个线程在第二个启动之前是否调用了wait()
。以上是关于boost::interprocess::interprocess_condition::wait 在等待时不会原子地解锁互斥锁的主要内容,如果未能解决你的问题,请参考以下文章