C++ std::lock_guard 示例,解释其工作原理
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/35252119/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
std::lock_guard example, explanation on why it works
提问by Deviatore
I've reached a point in my project that requires communication between threads on resources that very well may be written to, so synchronization is a must. However I don't really understand synchronization at anything other than the basic level.
我在我的项目中达到了一个点,需要在可以很好地写入的资源上的线程之间进行通信,因此必须进行同步。但是,除了基本级别之外,我并不真正了解同步。
Consider the last example in this link: http://www.bogotobogo.com/cplusplus/C11/7_C11_Thread_Sharing_Memory.php
考虑此链接中的最后一个示例:http: //www.bogotobogo.com/cplusplus/C11/7_C11_Thread_Sharing_Memory.php
#include <iostream>
#include <thread>
#include <list>
#include <algorithm>
#include <mutex>
using namespace std;
// a global variable
std::list<int>myList;
// a global instance of std::mutex to protect global variable
std::mutex myMutex;
void addToList(int max, int interval)
{
// the access to this function is mutually exclusive
std::lock_guard<std::mutex> guard(myMutex);
for (int i = 0; i < max; i++) {
if( (i % interval) == 0) myList.push_back(i);
}
}
void printList()
{
// the access to this function is mutually exclusive
std::lock_guard<std::mutex> guard(myMutex);
for (auto itr = myList.begin(), end_itr = myList.end(); itr != end_itr; ++itr ) {
cout << *itr << ",";
}
}
int main()
{
int max = 100;
std::thread t1(addToList, max, 1);
std::thread t2(addToList, max, 10);
std::thread t3(printList);
t1.join();
t2.join();
t3.join();
return 0;
}
The example demonstrates how three threads, two writers and one reader, accesses a common resource(list).
该示例演示了三个线程(两个写入者和一个读取者)如何访问公共资源(列表)。
Two global functions are used: one which is used by the two writer threads, and one being used by the reader thread. Both functions use a lock_guard to lock down the same resource, the list.
使用了两个全局函数:一个由两个写入线程使用,一个由读取线程使用。这两个函数都使用 lock_guard 来锁定相同的资源,即列表。
Now here is what I just can't wrap my head around: The reader uses a lock in a different scope than the two writer threads, yet still locks down the same resource. How can this work? My limited understanding of mutexes lends itself well to the writer function, there you got two threads using the exact same function. I can understand that, a check is made right as you are about to enter the protected area, and if someone else is already inside, you wait.
现在这就是我无法理解的事情:读取器在与两个写入器线程不同的范围内使用锁,但仍然锁定相同的资源。这怎么工作?我对互斥体的有限理解很适合编写器函数,在那里你有两个线程使用完全相同的函数。我可以理解,在你即将进入保护区的时候进行了检查,如果其他人已经在里面,你就等待。
But when the scope is different? This would indicate that there is some sort of mechanism more powerful than the process itself, some sort of runtime environment blocking execution of the "late" thread. But I thought there were no such things in c++. So I am at a loss.
但是当范围不同时呢?这表明存在某种比进程本身更强大的机制,某种运行时环境会阻塞“后期”线程的执行。但我认为 c++ 中没有这样的东西。所以我不知所措。
What exactly goes on under the hood here?
这里到底发生了什么?
采纳答案by Vikhram
myMutex
is global, which is what is used to protect myList
. guard(myMutex)
simply engages the lock and the exit from the block causes its destruction, dis-engaging the lock. guard
is just a convenient way to engage and dis-engage the lock.
myMutex
是全局的,这是用来保护myList
. guard(myMutex)
只需接合锁,块的出口就会导致其破坏,从而解除锁。guard
只是一种方便的方式来接合和解除锁定。
With that out of the way, mutex
does not protect any data. It just provides a wayto protect data. It is the design pattern that protects data. So if I write my own function to modify the list as below, the mutex
cannot protect it.
顺便说一句,mutex
不保护任何数据。它只是提供了一种保护数据的方法。它是保护数据的设计模式。因此,如果我编写自己的函数来修改如下列表,mutex
则无法保护它。
void addToListUnsafe(int max, int interval)
{
for (int i = 0; i < max; i++) {
if( (i % interval) == 0) myList.push_back(i);
}
}
The lock only works if all pieces of code that need to access the data engage the lock before accessing and disengage after they are done. This design-pattern of engaging and dis-engaging the lock before and after every access is what protects the data (myList
in your case)
只有当需要访问数据的所有代码段在访问之前锁定锁定并在完成之后解除锁定时,锁定才起作用。这种在每次访问之前和之后启用和解除锁定的设计模式是保护数据的方法(myList
在您的情况下)
Now you would wonder, why use mutex
at all, and why not, say, a bool
. And yes you can, but you will have to make sure that the bool
variable will exhibit certain characteristics including but not limited to the below list.
现在你会想知道,为什么要使用mutex
,为什么不使用,比如说,一个bool
. 是的,您可以,但您必须确保该bool
变量具有某些特征,包括但不限于以下列表。
- Not be cached (volatile) across multiple threads.
- Read and write will be atomic operation.
- Your lock can handle situation where there are multiple execution pipelines (logical cores, etc).
- 不会跨多个线程缓存(易失)。
- 读写将是原子操作。
- 您的锁可以处理存在多个执行管道(逻辑核心等)的情况。
There are different synchronization
mechanisms that provide "better locking" (across processes versus across threads, multiple processor versus, single processor, etc) at a cost of "slower performance", so you should always choose a locking mechanism which is just about enough for your situation.
有不同的synchronization
机制可以提供“更好的锁定”(跨进程与跨线程、多处理器与单处理器等),但代价是“性能较慢”,因此您应该始终选择一种足以满足您需求的锁定机制。情况。
回答by mindriot
Let's have a look at the relevant line:
让我们看一下相关的行:
std::lock_guard<std::mutex> guard(myMutex);
Notice that the lock_guard
references the globalmutex myMutex
. That is, the same mutex for all three threads. What lock_guard
does is essentially this:
请注意,lock_guard
引用了全局互斥锁myMutex
。也就是说,所有三个线程都使用相同的互斥锁。什么lock_guard
做基本上是这样的:
- Upon construction, it locks
myMutex
and keeps a reference to it. - Upon destruction (i.e. when the guard's scope is left), it unlocks
myMutex
.
- 在构建时,它会锁定
myMutex
并保留对它的引用。 - 在销毁时(即当守卫的范围离开时),它解锁
myMutex
。
The mutex is always the same one, it has nothing to do with the scope. The point of lock_guard
is just to make locking and unlocking the mutex easier for you. For example, if you manually lock
/unlock
, but your function throws an exception somewhere in the middle, it will never reach the unlock
statement. So, doing it the manual way youhave to make sure that the mutex is alwaysunlocked. On the other hand, the lock_guard
object gets destroyed automatically whenever the function is exited – regardless how it is exited.
互斥量总是相同的,它与作用域无关。重点lock_guard
只是让您更容易锁定和解锁互斥锁。例如,如果您手动lock
/ unlock
,但您的函数在中间某处抛出异常,它将永远不会到达该unlock
语句。所以,做手工的方式,你必须确保互斥量始终解锁。另一方面,lock_guard
只要函数退出,对象就会自动销毁——不管它是如何退出的。
回答by David Schwartz
This is precisely what a lock does. When a thread takes the lock, regardless of where in the code it does so, it must wait its turn if another thread holds the lock. When a thread releases a lock, regardless of where in the code it does so, another thread may acquire that lock.
这正是锁的作用。当一个线程获取锁时,无论它在代码中的哪个位置这样做,如果另一个线程持有锁,它必须等待轮到它。当一个线程释放锁时,无论它在代码中的哪个位置这样做,另一个线程都可能获得该锁。
Locks protect data, not code. They do it by ensuring all code that accesses the protected data does so while it holds the lock, excluding other threads from anycode that might access that same data.
锁保护的是数据,而不是代码。他们通过确保所有访问受保护数据的代码在持有锁时这样做,将其他线程排除在可能访问相同数据的任何代码之外。