- thread库
- thread只是类库,thread对象可以与实际运行的线程产生联系,利用Thread对象对线程进行操作,但是实际线程的存亡与thread对象的存亡并无绝对的决定关联
- 初始化构造函数:传入一个函数和它对应参数
- move构造函数:thread(thread&& x) noexcept,x所代表的线程的任务被交给thread对象
- join(): 操作是在thread t(func)后“某个地方”调用,其作用就是回收对应创建的线程的资源,避免造成资源的泄露
- detach():操作是在std::thread t(func)后马上调用,用于把被创建的线程与做创建动作的线程分离,分离的线程变为后台线程,其后,创建的线程的“死活”就与其做创建动作的线程无关,它的资源会被init进程回收。
- get_id():获取当前进程ID
- 下面是是两个进程数数的实例程序
-
#include <iostream> #include <utility> #include <thread> #include <chrono> #include <functional> #include <atomic> using namespace std; void f1(int& n) { int cnt = 10000; while (cnt--) { int a = n; int b = a + 1; n = b; } } void f2(int& n) { int cnt = 10000; while (cnt--) { int a = n; int b = a + 1; n = b; } } int main() { int n = 0; thread t1(f1, ref(n)); thread t2(f2, ref(n)); t1.join(); t2.join(); cout << "n === " << n << endl; return 0; }
- mutex系列类
- mutex代表互斥量,与C++11与mutex相关的类和函数都声明在<mutex>头文件
- std::mutex,最基本的mutex类
- std::time_mutex,定时 Mutex 类
- std::recursive_mutex,递归 Mutex 类。
- std::recursive_timed_mutex,定时递归 Mutex 类
- 函数
- try_lock,尝试对多个互斥量上锁
- lock,,同时对多个互斥量上锁
- std::call_once,如果多个线程需要同时调用某个函数,call_once 可以保证多个线程对该函数只调用一次
- mutex
- std::mutex 是C++11 中最基本的互斥量,std::mutex 对象提供了独占所有权的特性。
- 构造函数,std::mutex不允许拷贝构造,也不允许 move 拷贝,最初产生的 mutex 对象是处于 unlocked 状态的。
- lock(),调用线程将锁住该互斥量。线程调用该函数会发生下面 3 种情况:(1). 如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用 unlock之前,该线程一直拥有该锁。(2). 如果当前互斥量被其他线程锁住,则当前的调用线程被阻塞住。(3). 如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)。
- unlock(), 解锁,释放对互斥量的所有权。
- try_lock(),尝试锁住互斥量,如果互斥量被其他线程占有,则当前线程也不会被阻塞。线程调用该函数也会出现下面 3 种情况,(1). 如果当前互斥量没有被其他线程占有,则该线程锁住互斥量,直到该线程调用 unlock 释放互斥量。(2). 如果当前互斥量被其他线程锁住,则当前调用线程返回 false,而并不会被阻塞掉。(3). 如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)。
- 下面还是两个线程数数的程序
-
#include <thread> #include <iostream> #include <mutex> using namespace std; mutex mtx; void f1(int& n) { int cnt = 1000000; while (cnt--) { if (mtx.try_lock()) { int a = n; int b = a + 1; int c = b - 1; int d = c + 1; n = d; mtx.unlock(); } else { cnt++; } } } void f2(int& n) { int cnt = 1000000; while (cnt--) { if (mtx.try_lock()) { int a = n; int b = a + 1; int c = b - 1; int d = c + 1; n = d; mtx.unlock(); } else { cnt++; } } } int main() { int n = 0; thread t1(f1, ref(n)); thread t2(f2, ref(n)); t1.join(); t2.join(); cout << "n == " << n << endl; return 0; }
- recursive_mutex
- std::recursive_mutex 与 std::mutex 一样,也是一种可以被上锁的对象,但是和 std::mutex 不同的是,std::recursive_mutex 允许同一个线程对互斥量多次上锁(即递归上锁),来获得对互斥量对象的多层所有权,std::recursive_mutex 释放互斥量时需要调用与该锁层次深度相同次数的 unlock(),可理解为 lock() 次数和 unlock() 次数相同,除此之外,std::recursive_mutex 的特性和 std::mutex 大致相同。
- time_mutex
-
std::time_mutex 比 std::mutex 多了两个成员函数,try_lock_for(),try_lock_until()。
try_lock_for 函数接受一个时间范围,表示在这一段时间范围之内线程如果没有获得锁则被阻塞住(与 std::mutex 的 try_lock() 不同,try_lock 如果被调用时没有获得锁则直接返回 false),如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁并返回true,如果超时(即在指定时间内还是没有获得锁),则返回 false。
try_lock_until 函数则接受一个时间点作为参数,在指定时间点未到来之前线程如果没有获得锁则被阻塞住,如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁,如果超时(即在指定时间内还是没有获得锁),则返回 false。
-
没有获得锁的线程每隔200ms打印一个‘-’,程序如下所示
-
#include <iostream> // std::cout #include <chrono> // std::chrono::milliseconds #include <thread> // std::thread #include <mutex> // std::timed_mutex using namespace std; timed_mutex mtx; void f(int n) { while (!mtx.try_lock_for(chrono::milliseconds(200))) { cout << "-" << n; } this_thread::sleep_for(chrono::milliseconds(1000)); // 当一个线程获得锁之后就不会出现在上面的横杠里 cout << "*" << n << endl;; mtx.unlock(); } int main() { thread threads[10]; for (int i = 0; i < 10; i++) { threads[i] = thread(f, i); } for (int i = 0; i < 10; i++) { threads[i].join(); } return 0; }
-
- mutex代表互斥量,与C++11与mutex相关的类和函数都声明在<mutex>头文件
- condition_variable(条件变量)
- condition_variable实际上是一个类,是一个和条件相关的一个类,等待一个条件达成,这个类需要和互斥量来配合工作,用的时候我们要生成这个类的对象。
- wait()函数用于等待一个消息,他的第二个参数lambda表达式返回值是true,那wait()直接返回,否则wait()将解锁互斥量,并堵塞到本行,堵塞到其他某个线程调用notify_one()成员函数为止。如果wait()没有第二个参数,那么结果和lambda表达式返回false一样。
- 当线程调用notify_one()唤醒wait()(原先堵塞状态)时,wait()恢复后,不断地尝试重新获取互斥量锁,如果获取不到,那么流程卡在wait这里等着获取,如果获取到了锁,那么就判断wait()是都有第二个参数,如果有,那么判断lambda表达式值的真假,如果为true,则wait返回,流程走下去(此时互斥锁被锁着),如果为false那么wait对互斥量解锁,继续睡眠堵塞状态。如果没有结果和true的结果是一致的。
- 下面就是利用互斥量和条件变量实现的一个多线程队列
-
#include <iostream> #include <list> #include <mutex> #include <thread> #include <condition_variable> using namespace std; class MessageQueue { public: void Push() { for (int i = 0; i < 10000; i++) { cout << "Push " << i <<endl; unique_lock<mutex> lk(mtx); que.push_back(i); my_cond.notify_one(); } } void Pop() { while (true) { unique_lock<mutex> lk(mtx); my_cond.wait(lk, [this]()->bool {return !this->que.empty();}); int item = que.front(); que.pop_front(); cout << "Pop " << item << endl; } } private: mutex mtx; list<int> que; condition_variable my_cond; }; int main() { MessageQueue a; thread in(&MessageQueue::Push, &a); thread out(&MessageQueue::Pop, &a); in.join(); out.join(); return 0; }