是否有C ++设计模式实现控制线程拥有锁定资源的时间量的机制或互斥体?

问题描述 投票:16回答:5

我正在寻找一种方法来保证线程锁定特定资源的任何时候,它被强制在特定时间段之后释放该资源(如果它还没有释放它)。设想一个连接,您需要限制任何特定线程可以拥有该连接的时间。

我设想这是如何使用它:

{
    std::lock_guard<std::TimeLimitedMutex> lock(this->myTimeLimitedMutex, timeout);
    try {
        // perform some operation with the resource that myTimeLimitedMutex guards. 
    }
    catch (MutexTimeoutException ex) {
        // perform cleanup
    }
}

我看到有一个timed_mutex,如果无法获取锁,程序会超时。我需要在获取锁之后发生超时。

在某些情况下,您可以获得可以意外删除的资源。例如,tcp套接字 - 一旦建立套接字连接,每一侧的代码都需要处理另一方丢弃连接的情况。

我正在寻找一种处理通常超时的资源类型的模式,但是当它们没有时,它们需要被重置。这不必处理每种类型的资源。

c++ mutex deadlock
5个回答
33
投票

这不起作用,它永远不会起作用。换句话说,永远不可能做到这一点。它违背了所有权和原子交易的所有概念。因为当线程获取锁并在一行中实现两个事务时,它期望它们对于外部单词在原子上可见。在这种情况下,交易很可能会被撕裂 - 第一部分将被执行,但第二部分将不会执行。

更糟糕的是,由于锁定将被强制删除,在被中断的线程有机会回滚之前,部分执行的事务将对外部字体可见。

这个想法与所有多线程思维学派相悖。


15
投票

我支持谢谢谢谢。超时后释放锁定的互斥锁是个坏主意,无法正常工作。 Mutex代表互斥,这是一个不容易违反的坚硬合同。

但你可以做你想做的事:

问题:您希望保证线程不会将互斥锁保持的时间超过特定时间T.

解决方案:永远不要锁定互斥锁的时间超过时间T.而是编写代码,以便仅为绝对必要的操作锁定互斥锁。总是可以给出这样的时间T(当然,根据我的多任务处理和多用户操作系统来模拟不确定性和限制)。

为此(例子):

  • 切勿在锁定的部分内进行文件I / O.
  • 永久锁定互斥锁时,切勿呼叫系统呼叫。
  • 在互斥锁被锁定时避免对列表进行排序(*)。
  • 当互斥锁被锁定(*)时,避免对列表的每个元素执行慢速操作。
  • 在互斥锁被锁定(*)时避免内存分配/释放。

这些规则有例外,但一般准则是:

  • 使您的代码稍微不那么优化(例如在临界区内进行一些冗余复制),以使关键部分尽可能短。这是很好的多线程编程。

(*)这些只是操作的例子,它很有可能锁定整个列表,执行操作然后解锁列表。相反,建议只需获取列表的本地副本并在互斥锁被锁定时清除原始列表,理想情况下使用大多数STL容器提供的swap()操作。然后在关键部分之外的本地副本上执行慢速操作。这并非总是可行,但总是值得考虑。在最坏的情况下,排序具有方形复杂性,并且通常需要随机访问整个列表。在临界区之外对列表进行排序(副本)以及稍后检查是否需要添加或删除元素非常有用。内存分配也背后有一些复杂性,因此应避免大量内存分配/解除分配。


5
投票

你不能只用C ++做到这一点。

如果您使用的是Posix系统,则可以完成。你必须触发一个SIGALARM信号,该信号只对那些超时的线程进行掩码。在信号处理程序中,您必须设置一个标志并使用longjmp返回线程代码。在线程代码中,在setjmp位置,只有在触发信号时才能调用,因此可以抛出Timeout异常。

请参阅this answer了解如何做到这一点。

另外,在linux上,你可以直接从信号处理程序抛出seems(所以这里没有longjmp / setjmp)。

顺便说一句,如果我是你,我会编写相反的代码。想一想:你想告诉一个帖子“嘿,你花了太长时间,所以让我们抛弃你迄今为止所做的所有(长期)工作,这样我才能取得进步”。理想情况下,你应该让你的长线程更合作,做一些类似“我已经完成了ABCD任务的事情,让我们释放互斥锁,以便其他人可以在A上进行。然后让我们检查一下是否可以再次使用它做B和等等。“你可能想要更细粒度(在较小的对象上有更多的互斥锁,但确保你以相同的顺序锁定)或使用RW锁(如果你不修改它们,其他线程可以使用这些对象),等等...


1
投票

这种方法无法实施,因为互斥体的持有者需要有机会清理在事务中部分处于无效状态的任何事物。这可能需要一段未知的任意时间。

典型的方法是在执行长任务时释放锁定,并根据需要重新获取锁定。您必须自己管理,因为每个人都会采用略有不同的方法。

我知道这种事情被接受的唯一情况是在内核级别,特别是对于微控制器(它们没有内核,或者都是内核,取决于你问的是谁)。您可以设置一个修改调用堆栈的中断,这样当它被触发时,它会展开您感兴趣的特定操作。


0
投票

“条件”变量可能有超时。这允许您等到线程自愿释放资源(使用notify_one()或notify_all()),但等待本身将在指定的固定时间后超时。

Examples in the Boost documentation for "conditions"可能会更清楚。

如果要强制释放,则必须编写强制它的代码。这可能很危险。用C ++编写的代码可以做一些非常接近金属的东西。资源可能正在访问真正的硬件,它可能正在等待它完成某些事情。无论程序被困在什么地方,都可能无法实际结束。

但是,如果可能,那么您可以在wait()超时的线程中处理它。

© www.soinside.com 2019 - 2024. All rights reserved.