我们今天介绍一个简单的,用互斥上锁
那么为什么要这么做呢?
#include<iostream> #include<thread> void g(int& i) { std::cout << i++ << std::endl; } int main() { int n = 1; std::vector<std::thread>a; for (int i = 0; i < 10; i++) { a.emplace_back(g, std::ref(n)); } for (auto& i : a) { i.join(); } }
这是一个平平无奇的多线程的代码,因为资源竞争,它所打印的数据是不可预料的,可能有无数的线程同时在访问g函数或者打印。
我们的解决方式也很简单,上锁,先使用第一种方法
#include<iostream> #include<thread> #include<mutex> std::mutex I; void g(int& i) { I.lock(); std::cout << i++ << std::endl; I.unlock(); } int main() { int n = 1; std::vector<std::thread>a; for (int i = 0; i < 10; i++) { a.emplace_back(g, std::ref(n)); } for (auto& i : a) { i.join(); } }
直接就解决了,当线程访问此函数的时候遇到lock()就会上锁,等线程结束的时候unlock()才会解锁,让下一个线程进入访问,以此类推,很好理解。mutex对象的作用域需要比它们高,在类内的成员变量,或者全局变量都行。锁一般放的地方就是全局或者类成员
我们换一种方法使用std::lock_guard<std::mutex>
#include<iostream> #include<thread> #include<mutex> std::mutex I; void g(int& i) { std::lock_guard<std::mutex>a{ I }; std::cout << i++ << std::endl; } int main() { int n = 1; std::vector<std::thread>a; for (int i = 0; i < 10; i++) { a.emplace_back(g, std::ref(n)); } for (auto& i : a) { i.join(); } }
我们只改了一句话,在g函数,我们只需要一句就能完成之前两句的上锁,它的范围是作用域,生命周期,即可见函数内全部被上锁,在创建的使用调用构造函数,结束的时候析构
我们列举一下全部代码和C++20的jthread
#include<iostream>
#include<thread>
#include<vector>
#include<mutex>
std::mutex I;
void f(int& i) {
I.lock();
std::cout << i++ << std::endl;
I.unlock();
}
void g(int& i) {
std::lock_guard<std::mutex>a(I);
std::cout << i++ << std::endl;
}
int main() {
int n = 1;
std::vector<std::jthread>a;
for (int i = 0; i < 10; i++) {
//a.emplace_back(f, std::ref(n)); //这是lock版本的
a.emplace_back(g, std::ref(n));
}
/*for (auto& i : a) { //因为使用了C++20的jthread,所以不需要join了
i.join();
}*/
}
会用即可,可以去查看参考文档