提升进程间互斥并检查放弃

时间:2013-04-02 19:33:28

标签: c++ windows boost

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

我在测试中放弃了互斥锁,当然,当我使用scoped_lock来锁定互斥锁时,进程会无限期地阻塞。我想通过在scoped_lock上使用超时机制来解决这个问题(因为Googling花费了大量时间来解决这个问题并没有真正显示出来,因为可移植性原因,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 = reinterpret_cast<MyMutex*>(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 = reinterpret_cast<MyMutex*>(new MyMutex(boost::interprocess::open_or_create, "MutexName");
    }
}

这至少就是这个想法。三个有趣的观点:

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

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

我还应该提一下,由于这个硬件的工作原理,如果进程无法在10秒内获得互斥锁的所有权,那么互斥锁就会被废弃。事实上,我可能只需要等待50或60毫秒,但10秒钟是一个不错的“圆形”慷慨。

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

谢谢, 安迪

5 个答案:

答案 0 :(得分:5)

  

当我不使用超时对象,并且放弃了互斥锁时,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 lock实现的。查看此文档: 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中,文件描述符在未关闭之前保持有效,无论您是否已通过例如文件删除了文件。升压::进程间:: named_recursive_mutex ::删除。

答案 1 :(得分:4)

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

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

对不起,我现在无法帮助您处理代码;某些东西在那里工作不正常。

答案 2 :(得分:1)

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时才运行代码。

答案 3 :(得分:0)

boost :: interprocess :: named_mutex具有3个定义: 在Windows上,您可以使用宏来使用Windows互斥锁而不是Boost互斥锁,可以尝试捕获放弃的异常,并且应该对其进行解锁!

在Linux上,boost具有pthread_mutex,但在1_65_1版本中它不是可靠的属性

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

答案 4 :(得分:0)

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

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

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

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

问题是,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() < abs_time)

这只是推测,因为我无法快速访问 Windows 系统来对此进行测试。

有关详细信息,请参阅 https://www.boost.org/doc/libs/1_76_0/boost/interprocess/sync/detail/common_algorithms.hpp