7

我需要围绕一块硬件进行进程间同步。因为此代码需要在 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 上进行编译。

谢谢,安迪

4

5 回答 5

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 条目: 如何获得废弃的 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 这意味着,定时锁的实现可能会在内部抛出异常并且然后返回假。

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 删除了文件本身。

于 2013-04-02T20:07:02.963 回答
5

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

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

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

于 2013-07-09T07:12:13.473 回答
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() 为真时才运行代码。

于 2014-01-11T18:48:34.773 回答
1

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

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

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

于 2018-12-05T08:28:49.827 回答
0

克雷格格雷厄姆已经在回复中回答了这个问题,但我想我会详细说明,因为我发现了这个,没有阅读他的信息,而是用我的头来解决这个问题。

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

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

用户传递到 interprocessabs_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

于 2021-05-25T23:14:22.783 回答