c++线程库操作

一、函数介绍

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;
}

运行结果:
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值