根据the reference(匿名互斥示例),我使用boost::interprocess::shared_memory_object
在 Linux 上进行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
来电之前的行,一切正常。为什么?我不认为使用锁定的互斥锁进行睡眠会导致互斥锁被永久锁定。
我唯一的理论是,当内核唤醒服务器进程时,作用域结束并释放互斥锁,但等待进程没有机会运行。然后服务器重新获得锁定......但这似乎没有多大意义。
谢谢!
答案 0 :(得分:7)
你的理论是正确的。
如果你查看你链接的引用中匿名互斥示例的底部,你会看到
正如我们所看到的,互斥锁对于保护数据非常有用,但不会向另一个进程通知事件。
释放互斥锁并不会通知可能正在等待它的任何其他人,并且由于您的进程刚刚醒来,它几乎肯定有足够的调度量来做更多工作。它会在它再次休眠之前循环并重新获取互斥锁,这是客户端获取互斥锁本身的第一个机会。
将服务器sleep()
移到范围之外意味着它在互斥锁空闲时进入休眠状态,让客户端有机会自行运行并获取互斥锁。
如果您想放弃处理器,请尝试调用sched_yield()
(仅限Linux),但仍在您的范围内。 sleep(0)
也可能有用。
答案 1 :(得分:0)