优点:能够跨平台
线程创建
1.范例演示线程的开始运行和结束
#include <iostream>
#include <thread>
using namespace std;
void myprint()
{
cout << "子线程...." << endl;
}
int main()
{
thread t(myprint);
if(t.joinable())//判断是否能够使用join或者detach
{
//t.join();阻塞等待
t.detach();//线程分离,与join选择一个使用
}
cout << "main..." << endl;
return 0;
}
2.其他创建线程的手法
//仿函数
#include <iostream>
#include <thread>
using namespace std;
class T
{
public:
void operator()()
{
cout << "子线程...." << endl;
}
};
int main()
{
T t;//对象在主线程结束之后会不会出现问题?
//不会出现问题,t这个对象虽然不在了,但是thread这个类调用的是拷贝构造函数,是复制过去的,所以不会出问题
thread th(t);
th.detach();
cout << "主线程..." << endl;
return 0;
}
//lambda
int main()
{
auto th = []{
cout << "子线程" << endl;
}
thread t(th);
t.join();
cout << "主线程" << endl;
}
线程传参
传递临时对象作为线程参数
void myprint(const int &i,const string& s)//不用引用要调用三次对象,浪费严重
{
cout << i << endl;//这里的i不是一个真引用,而是一个假引用,它还是复制过来的,值传递
cout << s.c_str() << endl;//这里是将str转换为string的引用,也是开辟了一个新空间,但是有一个问题,就是万一转换之前主线程就已经结束了,就会出问题。
}
int main()
{
int a = 10;
int &ca = a;
char str[] = "test";
thread t(myprint,ca,str);//可能出问题的写法,此时str的对象是在子线程中构造的
thread t(myprint,ca,string(str));//一定不会出问题,一定会在主线程结束之前构造出来,此时str的对象会先在主线程中构造出来
t.detach();
return 0;
}
//thread t(myprint,std::ref(ca),str); 这个就是传引用,但是要注意一定要在子线程结束之后再结束主线程。
//这么记 标准库中的reference
class T
{
public:
void threadFun()
{
}
}
int main()
{
T a;
thread t(&T::threadfun,a);//类函数作为线程函数,先传类的函数的地址,再传一个具体的对象,后面有蚕食继续传参数
return 0;
}
创建和等待多个线程
void myprint(int i)
{
cout << "子线程" << i << endl;
}
int main()
{
vector<thread> threads;
for(int i = 0; i < 10; i++)
threads.push_back(thread(myprint,i));
for (auto it = v.begin(); it != v.end(); it++)
it->join();
return 0;
}
数据共享问题分析
1.只读的数据
是安全稳定的,不需要什么特别的处理手段。
2.有读有写
读时不写,写时不读。
几个出口就有几个unlock
std::lock_guard<mutex> g_mutex(my_mutex);
//构造里面lock,析构里面unlock,和智能指针有着异曲同工之妙,但有一个缺点,就是不能随性所欲的调用unlock
至少两个互斥量才会产生死锁。
保证两个互斥量加锁的顺序相同(同时先锁一,再锁二)。
std::lock()//函数模板,一次锁住两个或者两个以上的互斥量
//如果互斥量中有一个没锁住,就会在那里等着,等所有互斥量都锁住了,才往下走,如果只锁住了一把锁,就会立即解开。(处理多个互斥量出场)
std::lock(mutex1,mutex2);
//mutex1.unlock();
//mutex2.unlock();
lock_guard<std::mutex> guard1(mutex1,std::adopt_lock);//这样写也是自动解锁
lock_guard<std::mutex> guard2(mutex2,std::adopt_lock);//只是将lock的功能取消了
unique_lock
//较lock_guard灵活,常规使用方式一样
std::aodpt_lock;//用于第二个参数,表示互斥量已经被提前lock了,unique和guard都可以使用
std::try_to_lock;//尝试用mutex的lock()去锁定mutex,但如果没有锁定成功,我也会立即返回,并不会阻塞在那里。所以要使用这个api不能提前锁住。
std::unique_lock<mutex> ulock(my_mutex,std::try_to_lock);
if(ulock.owns.lock())//拿到了锁
else{
没拿到锁也能干点别的事情
}
std::defer_lock;//与try_to_lock的使用条件一样
//意思是初始化一个没有加锁的的mutex
//成员函数
std::unique_lock<mutex> ulock(my_mutex,std::defer_lock);
ulock.lock();//加锁,不用自己解锁
ulock.unlock();
if(ulock.try_lock())
else {没拿到锁干的事情}
std::mutex *p = ulock.release();//将和unique绑定的mutex分开,让两个之间不再有关系,返回mutex的指针,并且如果mutex在加锁状态,你有责任将它解锁
p->unlock();