std::lock_guard example, explanation on why it works
在我的项目中,我已经达到了一个要求,即在资源上的线程之间进行通信,而这些线程很可能被写入,所以同步是必须的。但是,除了基本级别之外,我不太了解同步。
请考虑此链接中的最后一个示例:http://www.bogotobogo.com/cplusplus/c11/7_c11_thread_sharing_memory.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | #include <iostream> #include <thread> #include <list> #include #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; } |
该示例演示了三个线程(两个编写器和一个读卡器)如何访问公共资源(列表)。
使用两个全局函数:一个由两个编写器线程使用,另一个由读线程使用。两个函数都使用锁保护来锁定同一个资源,即列表。
下面是我无法理解的地方:读卡器在不同于两个编写器线程的范围内使用锁,但仍然锁定相同的资源。这怎么办?我对互斥体的有限理解很适合编写函数,在这里有两个线程使用完全相同的函数。我可以理解,当你即将进入保护区时,检查是正确的,如果其他人已经在里面,你就等着。
但当范围不同时?这表明有某种机制比进程本身更强大,某种运行时环境阻止了"延迟"线程的执行。但我认为C++中没有这样的东西。所以我不知所措。
引擎盖下面到底发生了什么?
让我们看一下相关的行:
1 | std::lock_guard<std::mutex> guard(myMutex); |
注意,
- 施工时,锁定
myMutex 并保持参考。 - 一旦被摧毁(即当守卫的范围被留下时),它就会解锁
myMutex 。
互斥体总是相同的,与作用域无关。
在这种情况下,
1 2 3 4 5 6 | void addToListUnsafe(int max, int interval) { for (int i = 0; i < max; i++) { if( (i % interval) == 0) myList.push_back(i); } } |
只有当需要访问数据的所有代码在访问前都与锁接合,并在完成后分离时,锁才起作用。这种在每次访问前后启用和解除锁定的设计模式是保护数据的(在您的情况下为
现在你会想,为什么要使用
有不同的
这正是锁的作用。当一个线程获取锁时,不管它在代码中的哪个位置获取锁,如果另一个线程持有锁,它必须等待它的到来。当一个线程释放一个锁时,不管它在代码中的哪个位置释放锁,另一个线程都可能获取该锁。
锁保护数据,而不是代码。它们通过确保访问受保护数据的所有代码在持有锁的情况下都这样做,从可能访问相同数据的任何代码中排除其他线程。