简体   繁体   English

锁定多线程

[英]Locking multiple threads

I have a C++ program in a Win32 device. 我在Win32设备中有一个C ++程序。 The code has function X that should block other calls to X. That's simple enough, I can use a mutex to do that. 该代码具有X函数,该函数应阻止对X的其他调用。这很简单,我可以使用互斥锁来做到这一点。

Function X, however, creates and launches a thread, Y, that will monitor things after X has finished. 但是,函数X创建并启动线程Y,该线程将在X完成之后监视事物。 I need to ensure that X cannot run again until Y is satisfied that everything is finished properly. 我需要确保X不能再次运行,直到Y确信一切正确完成为止。

As I understand it, a mutex can only be acquired and released on the same thread. 据我了解,互斥锁只能在同一线程上获取和释放。 What I'd like to do is to hand over the 'locked-ness' of the mutex from X to Y. 我想做的就是将互斥锁的“锁定性”从X移交给Y。

If it's easier to picture this in terms of what actually happens, X is there to print something, Y is there to check that the print job completes without running out of paper. 如果更容易根据实际发生的情况对此进行描述,则X用于打印某些内容,Y用于检查打印作业是否完成而没有纸用完。 Once Y is satisfied that the job has completed and the paper has not run out then it can let X print something else. 一旦Y确信作业已完成并且纸张还没有用完,则可以让X打印其他内容。 We want X to finish ASAP so that the device can get on with other work (which will usually not involve printing, and therefore should not be held up while the printer finishes.) 我们希望X尽快完成,以便设备可以继续进行其他工作(通常不涉及打印,因此在打印机完成操作时不应拖延。)

So... is there a standard cross-thread locking pattern that will do what I want to do? 那么...是否有标准的跨线程锁定模式可以完成我想做的事情?

I can't use boost or any other third party libraries, only Windows built-in operations. 我不能使用boost或任何其他第三方库,只能使用Windows内置操作。

That's a simple enough scenario. 这是一个简单的场景。 All you need to do is replace the mutex with an auto-reset event , initially signaled. 您所需要做的就是用最初发出信号的自动重置事件替换互斥锁。

An auto-reset event can be used in the same way as a mutex (subject to some provisos, see below) but can be released by any thread: 自动重置事件可以与互斥锁相同的方式使用(受某些条款的约束,请参阅下文),但可以由任何线程释放:

  • The event starts out signaled ("unowned"). 事件开始时已发出信号(“未拥有”)。
  • To enter function X, wait on the event. 要输入功能X,请等待事件。 Only one thread will be allowed in, and the event will be cleared automatically. 仅允许一个线程进入,该事件将自动清除。
  • Function X starts thread Y, then exits without signaling the event. 函数X启动线程Y,然后退出而不通知事件。
  • At this point no thread can enter function X, not even the same thread that made the previous call. 此时,没有线程可以进入函数X,甚至没有进入上一个调用的线程。
  • When thread Y has completed its work, it signals the event. 线程Y完成工作后,将发出事件信号。 This will allow exactly one thread to enter function X. 这将只允许一个线程进入函数X。

There are a few differences between mutex objects and event objects that you should be aware of: 您应该注意互斥对象与事件对象之间的一些区别:

  • Unlike a mutex, an auto-reset event does not allow recursive entry by the same thread. 与互斥锁不同,自动重置事件不允许同一线程递归输入。 So if function X calls itself you'll need to rearrange the code slightly, so that acquiring the lock happens outside of the recursion. 因此,如果函数X调用自身,则需要稍微重新排列代码,以便在递归之外获取锁。

  • Unlike a mutex, the API will not generate an error if the thread that "owns" an event exits unexpectedly. 与互斥锁不同,如果“拥有”事件的线程意外退出,则API不会生成错误。 So if Y were to exit without signaling the event, the application will deadlock. 因此,如果Y在不通知事件的情况下退出,则应用程序将死锁。 If this is a problem, you will need to monitor the status of thread Y yourself. 如果出现问题,则需要自己监视线程Y的状态。 (And, of course, the same reasoning applies to the thread calling X, if it exits before launching Y.) (当然,如果调用X的线程在启动Y之前退出,则同样的理由也适用于调用X的线程。)

I would reverse the order of threads : 我会颠倒线程的顺序:

  • thread Y takes the mutex 线程Y接受互斥
  • thread Y starts thread X to do the printing 线程Y开始线程X进行打印
    • thread X does it printing and returns 线程X进行打印并返回
    • thread Y waits for its condition and monitors printing 线程Y等待其状态并监视打印
  • thread Y releases the mutex 线程Y释放互斥量

That way allows you to lock and release the mutex in same thread. 这样,您就可以在同一线程中锁定和释放互斥锁。 In addition you get a nicer separation of concerns : 另外,您可以更好地分离关注点:

  • X only deals with printing and does not even know of Y X仅处理打印,甚至不知道Y
  • Y is concerned by synchronization and proper printing end Y与同步和正确的打印结束有关

I marked Harry Johnston's answer as accepted because it sounds like it will do the job fine, and seems a good generic solution. 我将Harry Johnston的答案标记为已接受,因为这听起来像做得很好,而且似乎是一个很好的通用解决方案。 And I may even use it. 我什至可以使用它。

What I have done for now is to replace the single mutex with two critical section objects, let's calls them CSx and CSy. 我现在所做的是用两个关键部分对象替换单个互斥锁,我们将它们称为CSx和CSy。 Entry to X depends on entering CSx. 输入X取决于输入CSx。 Once that is done, X also enters CSy. 完成后,X也会输入CSy。 It then immediately leaves CSy, and this is because when thread Y is launched it will enter CSy for the duration of its life. 然后它立即离开CSy,这是因为启动线程Y后,它将在其生命周期内进入CSy。 The only reason X enters CSy (and leaves it immediately) is because that's how it knows that thread Y is not running from a previous call to X. X进入CSy(并立即离开)的唯一原因是因为它知道从先前对X的调用来看线程Y并未在运行。

This is similar to Erik's comment, above, but instead of tracking a single thread handle it allows multiple X and Y to queue up. 这与上面的Erik的评论类似,但是它不是跟踪单个线程句柄,而是允许多个X和Y排队。

The risk here is that thread Y may not enter CSy before function X leaves CSx, so there is scope for another X to get in first, though the circumstances of the device and what it is doing means that this won't actually happen. 这里的风险是线程Y可能不会在函数X离开CSx之前进入CSy,因此存在另一个X可以首先进入的空间,尽管设备的情况及其所执行的操作意味着这实际上不会发生。

Even so, Harry's solution has the benefit of a single locking object and the simplicity and elegance that this brings. 即便如此,Harry的解决方案仍然具有单个锁定对象的优点,并且带来了简洁和优雅。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM