如何保证一个函数在线程内没有返回就不会再次进入?
我不希望该函数被多个线程同时输入,也不希望它在尚未返回时再次输入。 有什么方法可以实现我的目标吗? 非常感谢!
I don't want the function to be entered simultaneously by multiple threads, neither do I want it to be entered again when it has not returned yet. Is there any approach to achieve my goal? Thank you very much!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
这两个目标都可以通过互斥信号量来实现。
Both goals can be achieved with a mutex semaphore.
正如其他答案所解释的那样,在一个线程上运行时阻止其他线程进入该函数非常简单。 但是如果你希望它在已经输入的情况下阻塞在同一个线程中......那么,这是一个死锁。
Blocking the function from being entered by other threads while it's in progress on one thread is pretty straightforward as explained by the other answers. But if you want it to block in the same thread when it's already been entered... well, that's a deadlock.
使用临界区(InitializeCriticalSection()、EnterCriticalSection()、LeaveCriticalSection())并实现入口计数器。 临界区将防止从不同线程重新进入,而条目计数器将防止从同一线程重新进入。
要实现条目计数器,请使用公共变量(适合您的情况的布尔值)和括号类。 一旦您已经进入临界区(因此没有其他线程将并行执行相同的代码),请检查变量的值。 如果它表明该函数已经进入 - 离开(首先释放临界区,然后离开该函数)。 否则构造将更改变量值的括号类实例。 因此,下次该线程进入该函数时,它将检查该变量,看看是否发生了重入并离开。 一旦你离开函数,括号类的析构函数会将变量更改为其原始值。
明智的做法是对关键部分条目和条目计数器更改使用括号类,以便您的代码是异常安全的,并且所有操作都按必要的顺序执行,无论您如何离开函数 - 在异常或返回语句上。
Use a critical section (InitializeCriticalSection(), EnterCriticalSection(), LeaveCriticalSection() ) and also implement an entry counter. The critical section will guard against reentry from different threads and the entry counter will guard against reentry from the same thread.
To implement an entry counter use a common variable (boolean for your case) and a bracket class. Once you've already entered the critical section (and therefore no other thread will execute the same code in parallel) check the value of the variable. If it states that the function has been entered already - leave (first release the critical section, then leave the function). Otherwise construct your bracket class instance that will change the variable value. So the next time this thread enters the function it will check the variable, see that reentry has happened and leave. The destructor of the bracket class will change the variable to its original value once you leave the function.
It's wise to use bracket classes for both the critical section entry and for the entry counter changing so that your code is exception safe and all actions are performed in necessary order and regardless of how yoou leave the function - on exception or on return statement.
既然您说的是 C++ 和 Windows,请查看 关键部分。 不过,为了便于使用,您可能希望将其包装在几个 C++ 类中。
如果锁已经被占用,关键部分会尝试短时间的自旋循环。 对于较短的代码片段,这通常可以避免进行完全阻塞等待,从而避免用户<>内核模式等的开销。
Since you're saying C++ and Windows, have a look at critical sections. You'll likely want to to wrap it in a couple of C++ classes, though, for ease of use.
Critical sections try spinlooping for a short duration, if the lock is already taken. For short pieces of code, this can often avoid doing a full blocking wait, and thus the overhead of user<>kernel mode etc.
f 只会在当前没有其他人运行时才被调用。
(这是仅使用 Win32 调用的概念演示)
请注意最少的检查、缺少互斥体的清理代码以及首次进入时的竞争条件。
f will only be called runs only, when nobody else is currently running it.
(This is concept demonstration with only Win32 calls)
Beware the minimal checking, the missing cleanup code for the mutex and the race-condition on first enter.
通常,您需要引入监视器,例如在 Java 中,通过将“synchronized”关键字添加到方法签名中。
(我对吗?)
Generally you need to introduce a monitor, e.g. in Java by adding the "synchronized" keyword to your method signature.
(Am I right?)
你可以这样做:
rank 是你的线程编号(假设你有编号为 0 到 size-1 的线程)。
这只是示例。 实际的实施会有所不同。 这取决于您想要使用什么库/函数来并行化您的代码(fork、MPI 等)。 但我希望它能给你一些有用的想法。
You can do something like this:
rank is your thread number (assumig you have threads with numbers 0 to size-1).
This is only example. Real implementation will be different. It depends on what library/functions you want to use to parallelize your code (fork, MPI etc). But I hope it gives you some useful thoughts.