一、函数介绍
1、构造函数

无参构造函数:
thread thd = thread(); 
有参构造函数:
template<class Fn, class... Arg> 
Fn:可调用对象(函数指针,仿函数,lambda表达式,包装器)
Arg:函数参数包
拷贝构造:
thread(const thread& th) = delete 
不希望我们拷贝对象
移动构造:
thread(thread&& th) 
支持移动构造
2、获取线程id
新线程因为有对象,所以直接调用函数 get_id()
主线程没有对象,用 this_thread::get_id()
3、其他函数
(1)jionable()
线程是否还在执行,joinable代表的是一个正在执行中的线程。
(2)jion()
该函数调用后会阻塞住线程,当该线程结束后,主线程继续执行
(3)detach()
在创建线程对象后马上调用,用于把被创建线程与线程对象分离开,分离 的线程变为后台线程,创建的线程的"死活"就与主线程无关
4、代码案例
void print(int n)
{
	for (int i = 0; i < n; i++)
	{
		cout << i << endl;
	}
}
int main()
{
	thread t1(print, 10);
	thread t2(print, 15);
	cout << "t1.get_id(): " << t1.get_id() << endl;
	cout << "t2.get_id(): " << t2.get_id() << endl;
	
	t1.join();
	t2.join();
	cout << "this thread: " << this_thread::get_id() << endl;
	return 0;
} 
运行结果:

二、认识锁
1、背景
int x = 0;
void print(int n)
{
	for (int i = 0; i < n; i++)
	{
		x++; 
	}
}
int main()
{
	thread t1(print, 10000);
	thread t2(print, 15000);
	t1.join();
	t2.join();
	cout << x << endl;
	return 0;
} 
预期结果应该是25000

这是因为多线程编程需要确保线程安全问题。
首先要明白线程拥有自己独立的栈结构,但对于全局变量等临界资源,是直接被多个线程共享的。这就会导致代码中的操作如果不是原子的线程(如 x++)安全就无法保障。
具体细节详见http://t.csdnimg.cn/zOevq
2、锁
(1)认识函数

引入锁的概念,如果一个线程竞争到锁,就会进入锁内部执行临界区代码,此时即使被切换,其他线程依然不会进入临界区,只能在临界区外等待锁被释放。
lock:竞争锁函数,如果竞争到锁就会返回,此时就可以向下执行代码。
unlock:解锁函数,执行完临界区代码解锁。
(2)代码案例
int x = 0;
mutex mtx;
void print(int n)
{
	//加锁建议在循环外
	mtx.lock();
	for (int i = 0; i < n; i++)
	{
		x++;
	}
	mtx.unlock();
}
int main()
{
	thread t1(print, 10000);
	thread t2(print, 15000);
	t1.join();
	t2.join();
	cout << x << endl;
	return 0;
} 
运行结果:
  

由于加锁之后能保证操作的原子性,结果一定是准确的。
int main()
{
	int x = 0;
	mutex mtx;
	//先创建空的线程
	vector<thread> ths(3);
	auto func = [&](int n) {
		mtx.lock();
		for (int i = 0; i < n; i++)
		{
			x++;
		}
		mtx.unlock();
	};
	
	for (auto& th : ths)
	{
		//构造匿名对象,由于之前创建了空的线程,
		//这里调用 operator= 移动赋值
		th = thread(func, 10000);
	}
	for (auto& th : ths)
	{
		th.join();
	}
	cout << x << endl;
	return 0;
} 
运行结果:
  

3、其他锁类型

(1)recursive_mutex
递归互斥锁,这把锁主要用来递归加锁的场景中,因为递归会引起死锁问题。
为什么会出现死锁?
 因为当前在进入递归函数前,申请了锁资源,进入递归函数后(还没有释放锁资源),再次申请锁资源,此时就会出现锁在我手里,但我还申请不到的现象,也就是死锁。
解决这个 死锁 问题的关键在于 自己在持有锁资源的情况下,不必再申请,此时就要用到recursive_mutex
(2)timed_mutex
时间互斥锁,这把锁中新增了定时解锁的功能,可以在程序运行指定时间后,自动解锁(如果还没有解锁的话)
(3)recursive_time_mutex
递归时间互斥锁,就是对timed_mutex时间互斥锁做了递归方面的升级,使其在面对递归场景时,不会出现死锁。
(4)RAII风格锁
a、lock_guard
锁守卫,只用lock, unlock会导致抛异常后难以解决死锁问题,lock_guard是一个类模板,构造时加锁,析构时解锁,如果执行管理代码的一部分可以加局部域 {}
b、unique_lock
特殊锁,类似于lock_guard,但是支持手动加锁解锁。

4、原子操作
是一个类模板,里面封装了大部分内置类型的++ -- ^ 等原子性操作,这样可以不用加锁来实现内置类型的一些原子操作。
内置类型如下:


获取参数用 load 函数
int main()
{
	vector<thread> vthd;
	int n;
	cin >> n;
	vthd.resize(n);
	atomic<int> x = 0;
	mutex mtx;
	auto func = [&](int n) {
		//mtx.lock();
		// 局部域
		{
			//lock_guard<mutex> lock(mtx);
			for (size_t i = 0; i < n; i++)
			{
				++x;
			}
			//mtx.unlock();
		}
	};
	for (auto& thd : vthd)
	{
		// 移动赋值
		thd = thread(func, 100000);
	}
	for (auto& thd : vthd)
	{
		thd.join();
	}
	printf("%d\n", x.load());
	return 0;
} 
运行结果:
  





















