C++11语言级别的多线程
关键字和语法方面
auto 可以根据右值推导出右值类型,然后左边变量类型就已知了
nullptr:指针专用,能够与指针进行区别,以前使用的NULL位define NULL 0
foreach(Type val:container)=>底层就是通过指针或者迭代器来实现的
{
cout<<val;
}
右值引用:move移动语义和forward类型完美转发函数
模板的一个新特新:typename... A 表示可变参数(类型参数)
智能指针,容器,绑定器和function都在之前的作品中
2. C++11语言级别的多线程
头文件<thread>
1. 如何启动一个线程
std::thread传入一个线程所需要的函数,然后后面为传入的参数,线程自动挡开启
2. 子线程如何结束
子线程函数运行完成,线程就自动结束了
3.主线程如何处理子线程
主线程等待子线程结束才可以继续运行(join),或者把子线程设置为分离线程(detach)
睡眠函数:阻塞2秒
std::this_thread::sleep_for(std::chrono::seconds(2));
线程之间的互斥操作
多线程程序存在竞态条件
多线程程序执行的结构是一致的,不会随着CPU对线程的调用顺序而产生不同的结果
线程间的互斥使用互斥锁
包含头文件mutex
#include <bits/stdc++.h>
using namespace std;std::mutex mtx;int mycount = 100;void sellTicket(int index){while(mycount>0){//锁+双重判断mtx.lock();if(mycount>0){cout<<"卖出第:"<<mycount<<"张票"<<endl;mycount--;mtx.unlock();this_thread::sleep_for(chrono::milliseconds(2));}}
}int main(){list<thread> tlist;for(int i=0;i<3;i++){tlist.push_back(thread(sellTicket,i));}for(auto &t:tlist){t.join();}return 0;
}
可以使用lock_guard<std::mutex> lock(mtx)代替mtx,这个会在出作用域的时候析构
note:lock_guard只能用在简单的临界区代码段的互斥操作中,构造时立即锁定互斥量,析构时自动解锁,不允许手动操作(如提前解锁)。
unique_lock //使用类似于unique_ptr
unique_lock //不仅可以在简单临界区代码段的互斥操作中,还能用在函数调用过程中
线程之间的同步通信机制
代码实现生产者消费者模型
#include <bits/stdc++.h>
using namespace std;mutex mtx;
//条件变量做线程之间的通信
std::condition_variable cv;
//实现生产者生产一个物品,消费者消费
//实现线程资源队列
class myQueue{
public:void put(int val){unique_lock<mutex> lck(mtx);while(!que.empty()){//que不为空,那么通知消费者区消费,消费者消费结束,再进行生产//生产者进入阻塞,并把锁释放掉unique_lock<mutex> lck(mtx);cv.wait(lck);//进入等待状态}que.push(val);cv.notify_all();//同时其它所有线程,生产了产品cout<<"生产者生产:"<<val<<endl;}int get(){unique_lock<mutex> lck(mtx);while(que.empty()){cv.wait(lck);//进入等待状态}int val = que.front();que.pop();cv.notify_all();cout<<"消费者消费:"<<val<<endl;return val;}
private:queue<int> que;
};
void producer(myQueue *que){for(int i=1;i<=10;++i){que->put(i);std::this_thread::sleep_for(std::chrono::milliseconds(100));}
}
void consumer(myQueue *que){for(int i=1;i<=10;++i){que->get();std::this_thread::sleep_for(std::chrono::milliseconds(100));}
}
int main(){myQueue que;std::thread t1(producer,&que);std::thread t2(consumer,&que);t1.join();t2.join();return 0;
}
notify_all告知等待所有线程,而notify_one告知一个线程
每个线程如果被wait阻塞,就会挂到一个阻塞队列中,然后通过notify进行唤醒
原子操作
互斥锁是比较重的,临界区代码做的事情稍稍复杂
系统理论,CAS保证上面++ --操作的原子特性就足够了
#include <bits/stdc++.h>
using namespace std;volatile atomic_bool isReady(false);
volatile atomic_int mycount(0);
void task(){while(!isReady){this_thread::yield();//线程让出当前CPU时间片,等待下一次调度}for(int i=0;i<100;i++){mycount++;}
}int main(){list<thread> tlist;for(int i=0;i<10;++i){tlist.emplace_back(task);}this_thread::sleep_for(chrono::seconds(3));isReady = true;for(auto &i:tlist){i.join();}cout<<"count:"<<mycount<<endl;return 0;
}