C++11引入了强大的线程库 std::thread
,相比于Linux下的pthread要方便许多。
头文件
<thread>
使用方法
先来一个示例
#include <thread>
#include <iostream>
using namespace std;
void t() {
cout << "t() Hello World" << endl;
}
int main() {
thread t1([]() {
cout << "lambda Hello World" << endl;
});
thread t2(t);
t1.join();
t2.join();
}
这段代码的作用就是创建两个线程,并在主线程内等待他们结束
thread这个类的构造函数可以接受一个函数指针或者lambda表达式,当我们thread类的对象创建,就会创建线程来执行我们传入的函数或lambda表达式。这个线程对象也会关联这个线程。
如果要给线程函数传参的话,直接在构造函数中传入就可以了
还是刚才的例子,如果f函数接受一个整型参数,那么这样写就可以了
thread t2(t, num);
如果还需要更多的参数的话,直接接在后边就行了。
在看以下thread的几个成员函数
- get_id() 获取线程id
- joinable() 线程是否还在执行,joinable代表的是一个正在执行中的线程。
- join() 该函数调用后会阻塞住线程,当该线程结束后,主线程继续执行
- detach() 在创建线程对象后马上调用,用于把被创建线程与线程对象分离开,分离的线程变为后台线程,创建的线程的"死活"就与主线程无关,就算主线程先结束了,对这个后台线程也没有影响。
它没有拷贝构造和拷贝赋值,但是有移动构造和移动赋值,可以将一个线程对象关联的线程转移给另一个线程对象
原子性操作库
C++11中引入了原子操作 atomic。
头文件 <atomic>
通过使用atomic,我们可以在不加锁的情况下,访问临界资源而没有一致性问题
看一个示例
#include <thread>
#include <iostream>
using namespace std;
// int a = 0;
//如果使用原子操作这样写就可以了
atomic<int> a(0);
void t(int n) {
for(int i = 0; i < n; i++) {
a++;
}
}
int main() {
thread t1(t, 100000);
thread t2(t, 100000);
t1.join();
t2.join();
cout << a << endl;
}
锁
- 互斥锁(Mutex)
头文件:< mutex >
通过构造std::mutex的对象创建互斥锁,调用成员函数lock()来锁定它,调用unlock()来解锁 - 条件锁
头文件:< condition_variable >
就是条件变量,配合 Mutex使用 - lock_guard(守卫锁)
头文件:< mutex >
通过构造和析构函数来自动控制加锁解锁
使用lock_guard<mutex> guard(mtx)
创建守卫锁,他会在作用域内自动给mtx加锁和解锁 - recursive_mutex 允许同一个线程对互斥量多次上锁(即递归上锁)
头文件:< mutex >