【问题标题】:Boost interprocess mutexes and checking for abandonment提升进程间互斥量并检查放弃
【发布时间】:2013-03-24 06:12:05
【问题描述】:

我需要围绕一个硬件进行进程间同步。因为此代码需要在 Windows 和 Linux 上运行,所以我使用 Boost Interprocess 互斥锁进行包装。一切正常,接受我检查放弃互斥锁的方法。有可能发生这种情况,所以我必须做好准备。

我在测试中放弃了互斥锁,果然,当我使用 scoped_lock 锁定互斥锁时,进程无限期地阻塞。我想解决这个问题的方法是在 scoped_lock 上使用超时机制(因为花费大量时间在谷歌上搜索解决这个问题的方法并没有真正显示出太多,由于可移植性的原因,boost 并没有做太多的事情)。

事不宜迟,这就是我所拥有的:

#include <boost/interprocess/sync/named_recursive_mutex.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>

typedef boost::interprocess::named_recursive_mutex MyMutex;
typedef boost::interprocess::scoped_lock<MyMutex> ScopedLock;

MyMutex* pGate = new MyMutex(boost::interprocess::open_or_create, "MutexName");

{
    // ScopedLock lock(*pGate); // this blocks indefinitely
    boost::posix_time::ptime timeout(boost::posix_time::microsec_clock::local_time() + boost::posix_time::seconds(10));
    ScopedLock lock(*pGate, timeout); // a 10 second timeout that returns immediately if the mutex is abandoned ?????
    if(!lock.owns()) {
        delete pGate;
        boost::interprocess::named_recursive_mutex::remove("MutexName");
        pGate = new MyMutex(boost::interprocess::open_or_create, "MutexName");
    }
}

至少,这是一个想法。三个有趣的点:

  • 当我使用超时对象并放弃互斥锁时,ScopedLock ctor 会无限期地阻塞。这是意料之中的。
  • 当我确实使用超时并且互斥体被放弃时,ScopedLock ctor 立即返回并告诉我它不拥有互斥体。好吧,也许这很正常,但它为什么不等我告诉它的 10 秒呢?
  • 当互斥锁没有被放弃,并且我使用超时时,ScopedLock ctor 仍然立即返回,告诉我它无法锁定或获取互斥锁的所有权,然后我就走了通过删除互斥体并重新制作它的动作。这根本不是我想要的。

那么,我在使用这些对象时缺少什么?也许它正盯着我的脸,但我看不到它,所以我在寻求帮助。

我还应该提到,由于该硬件的工作原理,如果进程无法在 10 秒内获得互斥锁的所有权,则该互斥锁将被放弃。事实上,我可能只需要等待 50 或 60 毫秒,但 10 秒是一个不错的“整数”慷慨数字。

我正在使用 Visual Studio 2010 在 Windows 7 上进行编译。

谢谢, 安迪

【问题讨论】:

  • 与您的问题无关,但您的示例中的reinterpret_cast&lt;MyMutex*&gt;s 不需要(不确定它们为什么存在)。
  • @GaborMarton 我认为我正确使用了 remove() 函数。再次检查代码。我正在删除指针,我同意它不会删除互斥锁,但是我确实调用了 boost::interprocess::named_recursive_mutex::remove("MyMutex")。如果我错误地使用它,请纠正我。谢谢
  • 检查this。它是原始强大的互斥体仿真的演变。
  • 我可能是错的,但根据我的经验,当进程间锁被放弃时,你唯一的希望就是关闭一切并退出。希望您可以在重新启动时恢复任何丢失的状态。换句话说,不要费心尝试继续运行。您的共享状态可能完全错误。
  • 有点晚了,但是对于其他走这条路的人,您需要使用universal_time()而不是local_time()。锁定超时是绝对 UTC 时间点。

标签: c++ windows boost


【解决方案1】:

当我不使用超时对象并放弃互斥锁时,ScopedLock ctor 会无限期地阻塞。这是预期的

如果 boost 支持健壮的互斥锁,则最好的解决方案是解决您的问题。然而 Boost 当前不支持健壮的互斥锁。只有一个计划来模拟强大的互斥锁,因为只有 linux 对此有本机支持。仿真仍然只是由图书馆作者 Ion Gaztanaga 计划的。 检查此链接,了解可能将 rubust 互斥体入侵到 boost 库中: http://boost.2283326.n4.nabble.com/boost-interprocess-gt-1-45-robust-mutexes-td3416151.html

同时,您可能会尝试在共享段中使用原子变量。

还可以看看这个 stackoverflow 条目: How do I take ownership of an abandoned boost::interprocess::interprocess_mutex?

当我使用超时并且互斥锁被放弃时,ScopedLock ctor 立即返回并告诉我它不拥有互斥锁。好吧,也许这很正常,但为什么不等我告诉它的 10 秒呢?

这很奇怪,你不应该得到这种行为。然而: 定时锁可能是用try锁来实现的。检查此文档: http://www.boost.org/doc/libs/1_53_0/doc/html/boost/interprocess/scoped_lock.html#idp57421760-bb 这意味着,定时锁的实现可能会在内部抛出异常,然后返回 false。

inline bool windows_mutex::timed_lock(const boost::posix_time::ptime &abs_time)
{
   sync_handles &handles =
      windows_intermodule_singleton<sync_handles>::get();
   //This can throw
   winapi_mutex_functions mut(handles.obtain_mutex(this->id_));
   return mut.timed_lock(abs_time);
}

可能是无法获取句柄,因为互斥体被废弃了。

当互斥锁没有被放弃并且我使用超时时,ScopedLock ctor 仍然立即返回,告诉我它无法锁定或获取互斥锁的所有权,我完成了删除互斥锁的动作并重新制作它。这根本不是我想要的。

我不确定这个,但我认为命名互斥锁是通过使用共享内存实现的。如果您使用的是 Linux,请检查文件 /dev/shm/MutexName。在 Linux 中,文件描述符在未被关闭之前保持有效,无论您是否已通过例如删除文件本身。 boost::interprocess::named_recursive_mutex::remove。

【讨论】:

  • 抱歉,在将其标记为答案时出现了可怕的延迟。基本上,由于 Boost 的可移植性,我们决定暂时解决这个“问题”。
【解决方案2】:

查看 BOOST_INTERPROCESS_ENABLE_TIMEOUT_WHEN_LOCKINGBOOST_INTERPROCESS_TIMEOUT_WHEN_LOCKING_DURATION_MS 编译标志。在代码中定义第一个符号以强制进程间互斥体超时,第二个符号定义超时持续时间。

我帮助将它们添加到库中以解决废弃的互斥锁问题。由于许多依赖于简单互斥而不是定时互斥的进程间构造(如 message_queue),因此有必要添加它。未来可能会有更强大的解决方案,但这个解决方案已经很好地满足了我的进程间需求。

很抱歉,我目前无法为您的代码提供帮助;那里有些东西不能正常工作。

【讨论】:

    【解决方案3】:

    BOOST_INTERPROCESS_ENABLE_TIMEOUT_WHEN_LOCKING 不太好。它引发异常并且没有多大帮助。为了解决异常行为,我编写了这个宏。对于通用目的,它可以正常工作。在此示例中,使用了 named_mutex。该宏创建一个具有超时的作用域锁,如果由于特殊原因无法获取锁,它会在之后解锁它。这样程序可以稍后再次锁定它,而不会立即冻结或崩溃。

    #define TIMEOUT 1000
    #define SAFELOCK(pMutex) \
        boost::posix_time::ptime wait_time \
            = boost::posix_time::microsec_clock::universal_time() \
            + boost::posix_time::milliseconds(TIMEOUT); \
        boost::interprocess::scoped_lock<boost::interprocess::named_mutex> lock(*pMutex, wait_time); \
        if(!lock.owns()) { \
            pMutex->unlock(); }
    

    但即使这样也不是最优的,因为要锁定的代码现在运行一次解锁。这可能会导致问题。但是,您可以轻松扩展宏。例如。仅当 lock.owns() 为 true 时才运行代码。

    【讨论】:

      【解决方案4】:

      boost::interprocess::named_mutex 有 3 个定义: 在windows上,你可以使用宏来使用windows mutex而不是boost mutex,你可以尝试捕获被遗弃的异常,你应该解锁它!

      在 linux 上,boost 有 pthread_mutex,但它在 1_65_1version 中不是健壮的属性

      所以我自己实现了 interprocess_mutex 使用系统 API(windows Mutex 和 linux pthread_mutex 进程共享模式),但是 windows Mutex 在内核而不是文件中。

      【讨论】:

        【解决方案5】:

        克雷格·格雷厄姆已经在回复中回答了这个问题,但我想我会详细说明,因为我发现了这个,没有阅读他的信息,而是拼命想弄清楚。

        在 POSIX 系统上,定时锁调用:

        timespec ts = ptime_to_timespec(abs_time);
        pthread_mutex_timedlock(&m_mut, &ts)
        

        其中abs_time 是用户传递给进程间timed_lockptime

        问题是,abs_time 必须是 UTC,而不是系统时间。 假设您要等待 10 秒;如果您领先于 UTC,您的 timed_lock() 将立即返回, 如果您落后于 UTC,您的 timed_lock() 将在 hours_behind - 10 秒后返回。

        以下ptime 在 10 秒内使进程间互斥体超时:

        boost::posix_time::ptime now = boost::posix_time::second_clock::universal_time() +
                                       boost::posix_time::seconds(10);
        

        如果我使用::local_time() 而不是::universal_time(),因为我领先于UTC,它会立即返回。 文档没有提到这一点。

        我没有尝试过,但是深入研究一下代码,看起来在非 POSIX 系统上也会出现同样的问题。

        如果没有定义BOOST_INTERPROCESS_POSIX_TIMEOUTS,则调用函数ipcdetail::try_based_timed_lock(*this, abs_time)。 它也使用世界时,等待while(microsec_clock::universal_time() &lt; abs_time)

        这只是猜测,因为我无法快速访问 Windows 系统来测试它。

        详情请见https://www.boost.org/doc/libs/1_76_0/boost/interprocess/sync/detail/common_algorithms.hpp

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-10-06
          • 2013-08-09
          • 1970-01-01
          • 1970-01-01
          • 2011-03-09
          • 2012-07-09
          相关资源
          最近更新 更多