【问题标题】:Recovering pthread_cond_t & pthread_mutex_t after process termination进程终止后恢复 pthread_cond_t 和 pthread_mutex_t
【发布时间】:2021-12-28 22:01:53
【问题描述】:

嘿!

我已经在这里查找了类似的问题,但没有得出最终的解决方案。

在我的应用程序中,我有两个同时运行的进程需要通过共享内存进行同步。目前我正在使用 pthread_mutex_tpthread_cond_t 用于该目的,它们被放入共享内存中。

这工作正常,直到进程 A 在等待条件时崩溃。如果进程 A 重新启动,则会发生死锁(?),其中进程 A 等待条件,而进程 B 在调用 pthread_cond_broadcast 时无限期卡住。

我读到这可能是由于互斥体处于不一致状态,但实际上它似乎从未出现在我的程序中。 如果您能告诉我我是否误解了某些东西,或者是否有解决此问题的替代方法,或者根本不可能保护这种崩溃情况,我将不胜感激。

struct Poller
{
    private:
      std::atomic<bool> waiting;
      pthread_mutex_t mtx;
      pthread_cond_t cnd;

    auto lockMutex() -> void
    {
      // Recover Mutex if any process locking it died unexpectedly
      LOG_DEBUG(info, "AdaptivePoller", "Locking mutex...");
      if(pthread_mutex_lock(&mtx) == EOWNERDEAD) {
        LOG_DEBUG(info, "AdaptivePoller", "Mutex in inconsistent state.");
        if(pthread_mutex_consistent(&mtx) != 0) {
          throw std::runtime_error("Mutex could not be brought back into consistent state.");
        }
      }
    }

    public:
      Poller()
      {
        waiting = false;
        
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST);
        pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
        pthread_mutex_init(&mtx, &attr);
        pthread_mutexattr_destroy(&attr);

        pthread_condattr_t attrcond;
        pthread_condattr_init(&attrcond);
        pthread_condattr_setpshared(&attrcond, PTHREAD_PROCESS_SHARED);
        pthread_cond_init(&cnd, &attrcond);
        pthread_condattr_destroy(&attrcond);
      }

      auto wait() -> void
      { 
        lockMutex();
        waiting = true;
        LOG_DEBUG(info, "AdaptivePoller", "Start waiting...");
        while(waiting) {
          pthread_cond_wait(&cnd, &mtx);
        }
        pthread_mutex_unlock(&mtx);
      }

      auto notify() -> void
      {    
        lockMutex();
        waiting = false;
        LOG_DEBUG(info, "AdaptivePoller", "Notifying...");
        pthread_cond_broadcast(&cnd);
        pthread_mutex_unlock(&mtx);
      }
};

【问题讨论】:

  • 你从pthread_mutex_lock中检查EOWNERDEAD,但不是从pthread_cond_wait,它在返回之前也获得了锁,可以返回EOWNERDEAD。如果有帮助,请告诉我(在评论中使用@ikegami)。
  • POSIX 不为其条件变量提供稳健性选项。众所周知,实现会有所不同,但在最近的 glibc 中,例如,如果进程在进程共享 CV 上的 pthread_cond_wait() 中被阻塞时死亡,则该 CV 的状态不可恢复。
  • @JohnBollinger 感谢您提供的信息!你知道/你能推荐另一种方法来实现类似的同步行为,比如在我的应用程序中,并且能够正确处理这种极端情况吗?
  • Related stackoverflow.com/questions/69650814/… 也许可以拉伸成副本。
  • @Varrick,我看到的同步行为是一个或多个线程可以自己阻塞,直到稍后另一个线程解除阻塞所有等待的线程。这是对您想要实现的目标的准确和充分的描述吗?

标签: c++ pthreads ipc shared-memory


【解决方案1】:

据我所知,POSIX API 没有定义足够的机制来使您的Poller 对所有进程故障情况完全稳健。但是您可能会通过使用 SysV 信号量代替条件变量和互斥体来实现您认为可以改进的情况。这是一个大纲:

  • 信号量can_proceed 提供线程阻塞自己直到被释放。该信号量的初始值为 0,线程通过尝试递减它来阻塞自己。

  • 线程通过将can_proceed 增加阻塞进程的数量来释放当前阻塞的进程。 SysV 信号量 API 提供了一个操作来确定它是多少。

以上内容并非万无一失。至少会发生以下情况:

  • 一个等待进程 W 在通知进程 N 读取了一个包含 W 的等待者计数之后死亡,但在 W 之前em> 完成其信号量减量。这将允许一位未来的服务员通过而不会阻塞。

  • 等待进程 W2 在通知进程增加 can_proceed 和最后一个先前等待的进程完成其信号量减量之间到达 wait()。在这种情况下,W2 可能会立即继续,而不是在通知时已经在等待的某个线程 W1。在这种情况下,W1 将被下一个通知释放(除非同样的事情再次发生)。

  • 如果两个进程几乎同时尝试通知,则可以观察到反映已释放但尚未完成其信号量减量的进程的服务员计数。可能的结果是一个或多个未来进程将通过wait() 而不会阻塞。

由于其中两个类似于 CV 等待的虚假返回,因此可以通过类似的谓词检查习语来解决它们。考虑到进程完成其信号量减量和它获取互斥锁之间必然存在间隙,这将是棘手的,但也许可以通过使用原子对象来解决这个问题,因此不需要互斥锁。

在增加can_proceed 之前,进程在notify() 中终止也是可能的,因此该通知无效。我不认为这是建议方案特有的弱点。

请注意,SysV 信号量本身并不驻留在共享内存中,也不需要共享内存。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-11
    • 2015-10-24
    • 1970-01-01
    • 1970-01-01
    • 2021-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多