在持有 boost::interprocess::scoped_lock 时休眠会导致它永远不会被释放

Posted

技术标签:

【中文标题】在持有 boost::interprocess::scoped_lock 时休眠会导致它永远不会被释放【英文标题】:Sleep while holding a boost::interprocess::scoped_lock causes it to be never released 【发布时间】:2009-06-10 18:07:07 【问题描述】:

我在 Linux 上使用 boost::interprocess::shared_memory_object 按照 the reference(匿名互斥锁示例)进行 IPC。

有一个服务器进程,它创建shared_memory_object 并写入它,同时将interprocess_mutex 包裹在scoped_lock 中;以及一个打印其他人编写的任何内容的客户端进程——在这种情况下,它是一个int

我遇到了一个问题:如果服务器在持有互斥锁时休眠,则客户端进程永远无法获取它并永远等待。

Buggy 服务器循环:

using namespace boost::interprocess;
int n = 0;
while (1) 
    std::cerr << "acquiring mutex... ";
    
        // "data" is a struct on the shared mem. and contains a mutex and an int
        scoped_lock<interprocess_mutex> lock(data->mutex);
        data->a = n++;
        std::cerr << n << std::endl;
        sleep(1);
     // if this bracket is placed before "sleep", everything works

服务器输出:

acquiring mutex... 1
acquiring mutex... 2
acquiring mutex... 3
acquiring mutex... 4

客户端循环:

while(1) 
   std::cerr << "acquiring mutex... ";
   
      scoped_lock<interprocess_mutex> lock(data->mutex);
      std::cerr << data->a << std::endl;
   
   sleep(1);

客户端输出(永远等待):

acquiring mutex...

问题是,如果我将括号移到sleep 调用之前的行,一切正常。为什么?我不认为与锁定的互斥锁一起睡觉会导致互斥锁永远被锁定。

我唯一的理论是,当内核唤醒服务器进程时,作用域结束并释放互斥锁,但等待进程没有机会运行。然后服务器重新获取锁......但这似乎没有多大意义。

谢谢!

【问题讨论】:

【参考方案1】:

你的理论是正确的。

如果您查看链接参考中匿名互斥锁示例的底部,您会看到

如我们所见,互斥锁可用于保护数据,但不能将事件通知给另一个进程。

释放互斥锁不会通知可能正在等待它的任何其他人,而且由于您的进程刚刚唤醒,几乎可以肯定它还有大量的调度量可以做更多的工作。它将在再次休眠之前循环并重新获取互斥锁,这是客户端必须自己获取互斥锁的第一个机会。

将服务器 sleep() 移出范围意味着它在互斥锁空闲时进入睡眠状态,让客户端有机会运行并为自己获取互斥锁。

如果您想放弃处理器,但仍要在您的范围内休眠,请尝试调用 sched_yield()(仅限 Linux)。 sleep(0) 也可以工作。

【讨论】:

Boost 确实有自己的调用来放弃线程(如 linux 的 schedu_yield): boost::this_thread::yield() 谢谢。 sleep(0) 确实有效,这证实了这一理论。我的印象是释放互斥锁的行为至少会导致系统调用并因此重新调度,但我错了。【参考方案2】:

拿着互斥锁睡觉是错误的。互斥锁保护一些数据(即 data->a),并且应该在读取/写入该数据时最小化范围。

【讨论】:

我认为它确实应该;似乎史蒂夫在理解真正的问题方面做得更好。

以上是关于在持有 boost::interprocess::scoped_lock 时休眠会导致它永远不会被释放的主要内容,如果未能解决你的问题,请参考以下文章

boost::interprocess::interprocess_condition::wait 在等待时不会原子地解锁互斥锁

boost::interprocess_mutex 与进程本地 boost::mutex

Boost.interprocess Vector 作为类成员

如何通过 boost::interprocess 在向量中构造向量

boost::interprocess::string 转换为 char*

这些 Boost::Interprocess 组件是不是需要同步?