锁定多个线程
Posted
技术标签:
【中文标题】锁定多个线程【英文标题】:Locking multiple threads 【发布时间】:2015-04-28 15:07:48 【问题描述】:我在 Win32 设备中有一个 C++ 程序。代码有函数 X,它应该阻止对 X 的其他调用。这很简单,我可以使用互斥体来做到这一点。
然而,函数 X 创建并启动一个线程 Y,它将在 X 完成后监视事物。我需要确保 X 不能再次运行,直到 Y 确信一切都已正确完成。
据我了解,只能在同一个线程上获取和释放互斥锁。我想做的是将互斥锁的“锁定性”从 X 移交给 Y。
如果根据实际发生的情况更容易地描述这一点,X 用于打印某些内容,Y 用于检查打印作业是否在没有用完纸张的情况下完成。一旦 Y 对作业已经完成并且纸张没有用完感到满意,它就可以让 X 打印其他东西。我们希望 X 尽快完成,以便设备可以继续进行其他工作(通常不涉及打印,因此不应在打印机完成时暂停。)
那么...是否有一个标准的跨线程锁定模式可以做我想做的事?
我不能使用 boost 或任何其他第三方库,只能使用 Windows 内置操作。
【问题讨论】:
也许是一个计时器而不是一个线程? 线程 Y 将一直运行直到打印作业完成,这可能包括通知用户纸张用完并等待新纸张可用,即它可能需要很长时间并且不可预测足以让计时器提供帮助。 线程 X 和 Y 的 Synchronization Barrier 可用于实现需求。 简单的解决方案:检查线程是否使用 WaitForSingleObject(hThread) 运行 semaphore 怎么样? 【参考方案1】:这是一个足够简单的场景。您需要做的就是将互斥锁替换为 auto-reset event,最初发出信号。
自动重置事件的使用方式与互斥锁相同(受一些限制条件,见下文),但可以由任何线程释放:
事件开始时发出信号(“unowned”)。 要进入函数 X,请等待事件。只允许一个线程进入,事件将被自动清除。 函数 X 启动线程 Y,然后退出而不发出事件信号。 此时没有线程可以进入函数 X,甚至是之前调用的同一个线程。 当线程 Y 完成其工作时,它会发出事件信号。这将只允许一个线程进入函数 X。您应该注意互斥对象和事件对象之间的一些区别:
与互斥锁不同,自动重置事件不允许同一线程递归进入。因此,如果函数 X 调用自身,您需要稍微重新排列代码,以便在递归之外获取锁。
与互斥锁不同,如果“拥有”事件的线程意外退出,API 不会生成错误。因此,如果 Y 在没有发出事件信号的情况下退出,应用程序将死锁。如果这是一个问题,您将需要自己监控线程 Y 的状态。 (当然,同样的推理也适用于调用 X 的线程,如果它在启动 Y 之前退出。)
【讨论】:
【参考方案2】:我会颠倒线程的顺序:
线程 Y 使用互斥体 线程 Y 启动线程 X 进行打印 线程 X 打印并返回 线程 Y 等待其状态并监控打印 线程 Y 释放互斥体这种方式允许您在同一个线程中锁定和释放互斥锁。此外,您还可以更好地分离关注点:
X 只处理打印,甚至不知道 Y Y 关心的是同步和正确的打印结束【讨论】:
我喜欢这个想法,虽然它是一个双线程解决方案,而不是一个创建单线程的函数。所以在这里启动线程 Y 的代码必须等到 X 完成,然后我们仍然必须确保在我们等待时不能启动第二个 Y。但我会考虑更多,因为我喜欢关注点分离。 @ColinM :如果您认为只需要一个新线程,Y 可能是在调用线程中运行的函数。我的解决方案只是反转调用打印机的监视器,而不是相反。【参考方案3】:我将 Harry Johnston 的答案标记为已接受,因为听起来它可以很好地完成工作,并且似乎是一个很好的通用解决方案。我什至可以使用它。
我现在所做的是将单个互斥锁替换为两个临界区对象,我们称它们为 CSx 和 CSy。进入 X 取决于进入 CSx。一旦完成,X 也进入 CSy。然后它立即离开 CSy,这是因为当线程 Y 启动时,它将在其生命周期内进入 CSy。 X 进入 CSy(并立即离开)的唯一原因是因为它知道线程 Y 没有从先前对 X 的调用中运行。
这类似于上面 Erik 的评论,但不是跟踪单个线程句柄,而是允许多个 X 和 Y 排队。
这里的风险是线程 Y 可能不会在函数 X 离开 CSx 之前进入 CSy,因此另一个 X 有先进入的空间,尽管设备的情况和它正在做什么意味着这实际上不会发生。
即便如此,Harry 的解决方案仍具有单一锁定对象的优势,以及由此带来的简单和优雅。
【讨论】:
不赞成就接受答案看起来不对。以上是关于锁定多个线程的主要内容,如果未能解决你的问题,请参考以下文章
shared_ptr.get() 是不是可以被多个线程调用,而另一个线程锁定并调用 shared_ptr.swap()?