锁定多个线程

Locking multiple threads

我在 Win32 设备中有一个 C++ 程序。该代码具有应该阻止对 X 的其他调用的函数 X。这很简单,我可以使用互斥体来做到这一点。

然而,函数 X 创建并启动一个线程 Y,它将在 X 完成后监视事情。我需要确保 X 不能再次 运行,直到 Y 对一切都正确完成感到满意为止。

据我了解,只能在同一线程上获取和释放互斥量。我想做的是把互斥体的'locked-ness'从X交给Y。

如果根据实际发生的情况更容易描述,X 用于打印某些东西,Y 用于检查打印作业是否完成且 运行 没有纸张。一旦 Y 对作业已经完成并且纸张还没有 运行 感到满意,它就可以让 X 打印其他东西。我们希望 X 尽快完成,以便设备可以继续其他工作(这通常不涉及打印,因此在打印机完成时不应暂停。)

那么...是否有标准的跨线程锁定模式可以完成我想做的事情?

我不能使用 boost 或任何其他第三方库,只能使用 Windows 内置操作。

我会颠倒线程的顺序:

  • 线程 Y 获取互斥量
  • 线程 Y 启动线程 X 进行打印
    • 线程 X 进行打印,returns
    • 线程 Y 等待其条件并监视打印
  • 线程 Y 释放互斥量

这种方式允许您在同一个线程中锁定和释放互斥量。此外,您可以获得更好的关注点分离:

  • X只处理打印,连Y都不知道
  • Y 关注同步和正确的打印结束

这是一个足够简单的场景。您需要做的就是用 auto-reset event 替换互斥锁,最初是有信号的。

自动重置事件可以像互斥锁一样使用(受一些限制条件,见下文)但可以由任何线程释放:

  • 事件开始时发出信号 ("unowned")。
  • 要进入功能 X,请等待事件。只允许一个线程进入,事件将自动清除。
  • 函数 X 启动线程 Y,然后 在没有 发出事件信号的情况下退出。
  • 此时没有线程可以进入函数 X,即使是进行上一次调用的线程也不行。
  • 当线程 Y 完成其工作时,它发出事件信号。这将允许恰好一个线程进入函数 X。

互斥对象和事件对象之间存在一些您应该注意的区别:

  • 与互斥体不同,自动重置事件不允许同一线程递归进入。因此,如果函数 X 调用自身,您需要稍微重新安排代码,以便在递归之外获取锁。

  • 与互斥锁不同,如果线程 "owns" 事件意外退出,API 不会产生错误。因此,如果 Y 在没有发出事件信号的情况下退出,应用程序将死锁。如果这是一个问题,您将需要自己监视线程 Y 的状态。 (当然,同样的推理适用于调用 X 的线程,如果它在启动 Y 之前退出。)

我将 Harry Johnston 的回答标记为已接受,因为它听起来可以很好地完成工作,而且似乎是一个很好的通用解决方案。我什至可以使用它。

我现在所做的是用两个关键部分对象替换单个互斥锁,我们称它们为 CSx 和 CSy。进入 X 取决于进入 CSx。一旦完成,X 也进入 CSy。然后它立即离开 CSy,这是因为当线程 Y 启动时,它将在其生命周期内进入 CSy。 X 进入 CSy(并立即离开)的唯一原因是因为这就是它知道线程 Y 不是来自先前对 X 的调用的 运行。

这与上面 Erik 的评论类似,但它不是跟踪单个线程句柄,而是允许多个 X 和 Y 排队。

这里的风险是线程 Y 可能不会在函数 X 离开 CSx 之前进入 CSy,因此另一个 X 可以先进入,尽管设备的情况和它正在做的事情意味着这不会'实际上并没有发生。

即便如此,Harry 的解决方案还是具有单一锁定对象的优势,以及由此带来的简单和优雅。