posix: interprocess lock abandoned,有没有更好的办法?

posix: interprocess lock abandoned, is there a better way?

我在 AIX 上编写代码,但正在寻找通用的 'nix 解决方案,理想情况下 posix 兼容。不能在 C++11 或更高版本中使用任何东西。

我与涉及的许多进程的许多线程共享内存。共享内存中的数据要保持自洽,所以我需要一把锁,让大家轮流使用。

进程因锁而崩溃是一回事,所以我必须能够检测到被遗弃的锁,修复(也称为重置)数据,然后继续。 Twist:通过等待一段时间来决定放弃锁不是一个可行的解决方案。

全局互斥锁(驻留在共享内存中或已命名)似乎不是解决方案。没有放弃检测机制(时间除外),即使那样你也不能在不冒未定义行为的风险的情况下删除和改造互斥体。

所以我选择了 lockf() 和一个忙标志 - 获取文件锁,在共享内存中设置标志,执行操作,取消设置标志,删除锁。在拥有锁的情况下崩溃时,锁会自动删除,下一个拿到锁的人可以看到忙标志仍然设置,并且知道他必须清理一团糟。

这不起作用 - 因为 lockf() 会将其他进程的线程拒之门外,但它对您自己进程中的其他线程具有特殊语义。它让他们不受限制地通过。

最后我想到了一个两步解决方案——一个本地(线程)互斥锁和一个文件锁。先获取本地互斥锁;现在您是该进程中唯一执行下一步的线程,即 lockf()。 lockf() 反过来保证你是唯一通过的进程,所以现在你可以设置忙碌标志并完成工作。要解锁,按相反的顺序进行:清除忙标志、删除文件锁、删除互斥锁。在崩溃时,本地互斥锁会在进程崩溃时消失,所以它是无害的。

工作正常。我恨它。使用像这样嵌套的两个锁让我觉得很昂贵,并且需要在代码中花一页的注释来解释。 (我的下一次代码审查会很有趣)。我觉得我错过了一个更好的解决方案。这是什么?

编辑:@Matt 我可能不清楚。忙标志不是锁定机制的一部分;它在那里指示某个进程何时成功获取了锁。如果在获取锁之后,您看到 busy 标志已经设置,这意味着其他进程获得了锁然后崩溃,使它正在写入的共享内存处于不完整状态。在那种情况下,现在拥有锁的线程将完成将共享内存重新初始化为可用状态的工作。我可能应该称它为 "memoryBeingModified" 标志。

"tryLock" 的任何变化都是不允许的。在这个应用程序中轮询是绝对不可能的。需要修改共享内存的线程可能只会阻塞在锁上(锁永远不会持有太久),并且必须在锁可用时立即轮到它们。他们必须经历尽可能少的延迟。

你可以

//always returns true unless something horrible happened
bool lock()
{
    if (pthread_mutex_lock(&local_mutex)==0)
    {
        if (lockf(global_fd, F_LOCK, 0))
            return true;
        pthread_mutex_unlock(&local_mutex);
    }
    return false;
}

void unlock()
{
    lockf(global_fd, F_ULOCK, 0);
    pthread_mutex_unlock(&local_mutex);
}

这对我来说似乎很简单,使用 2 级锁我也不会觉得太糟糕 -- pthread_mutex 非常快并且几乎不消耗资源。

简单的答案是,没有好的解决方案。在 AIX 上,lockf 被证明是极其缓慢的,没有充分的理由。但是共享内存中的互斥量虽然在任何平台上都非常快,但很脆弱(任何人都可能在持有锁时崩溃并且无法恢复。)posix 定义一个 "this mutex is held by a thread/process that died " 会很好,但是它没有,即使有这样的错误代码,也没有办法修复并继续。与多个读者和作者一起使用共享内存仍然是狂野的西部。