C++ 11 线程库笔记 mutex锁 原子操作

1、线程函数

 C++11的多线程必须包含< thread >头文件,同时,而且在原子操作中还引入了原子类的概念。
 一些相关函数
thread() 构造一个线程对象,没有关联任何线程函数,即没有启动任何线程。
thread[] 构造一个线程数组,没有关联任何线程函数,即没有启动任何线程。
thread(fn,args,…) 构造一个线程对象,并关联线程函数fn,args1,args2,…为线程函数的参数。
get_id() 获取线程id。
jionable() 线程是否还在执行,joinable代表的是一个正在执行中的线程。
jion() 该函数调用后会阻塞住线程,当该线程结束后,主线程继续执行。
detach() 在创建线程对象后马上调用,用于把被创建线程与线程对象分离开,分离的线程变为后台线程,创建的线程的"死活"就与主线程无关。
注意:
1、 线程是操作系统中的一个概念,线程对象可以关联一个线程,用来控制线程以及获取线程的状态。
2. 当创建一个线程对象后,没有提供线程函数,该对象实际没有对应任何线程。

int main()
{
	std::thread t1;
	cout << t1.get_id() << endl;
	return 0;
}

该线程id为0。
而在关联了一个线程后,可以输出该线程id。

void ThreadFunc(int a)
{
	cout << "Thread1" << a << endl;
}
int main()
{
	std::thread t1(ThreadFunc,10);
	cout << t1.get_id() << endl;
	t1.join();
	return 0;
}

对线程数组初始化

#include <iostream>       // std::cout
#include <thread>         // std::thread, std::this_thread::sleep_for
#include <chrono>         // std::chrono::seconds
 
void pause_thread(int n) 
{
  std::this_thread::sleep_for (std::chrono::seconds(n));
  std::cout << "pause of " << n << " seconds ended\n";
}

int main() 
{
  std::thread threads[5];                         // default-constructed threads

  std::cout << "Spawning 5 threads...\n";
  for (int i=0; i<5; ++i)
    threads[i] = std::thread(pause_thread,i+1);   // move-assign threads

  std::cout << "Done spawning threads. Now waiting for them to join:\n";
  for (int i=0; i<5; ++i)
    threads[i].join();

  std::cout << "All threads joined!\n";

  return 0;
}

1.1 线程传参

 要想改变参数,要么以引用传参,要么以指针传参。引用传参必须是使用ref(a)传过去,这是因为线程也会存在一个线程栈,传参过去其实中间会产生一个临时拷贝tmp,随后再用tmp初始化参数列表。而如果直接使用thread t1(ThreadFunc1, a)传过去,随后使用void ThreadFunc1(int& x)接收,程序会报错,因为线程的执行是通过临时对象传递的,而临时对象是能引用的。而ref(a)是把引用变成指针存起来,用的时候再转成引用。

void ThreadFunc1(int& x)
{
	x += 10;
}
void ThreadFunc2(int* x)
{
	*x += 10;
}
void ThreadFunc(int a)
{
	cout << "Thread1" << a << endl;
}
int main()
{
	int a = 10;
	// 在线程函数中对a修改,不会影响外部实参,因为:线程函数参数虽然是引用方式,但其实际引用的
	//是线程栈中的拷贝
	//thread t1(ThreadFunc1, a);不能直接传入a,程序会报错,两种方法,1、ref(a)  2、右值引用void ThreadFunc1(int&& x)
	//t1.join();
	//cout << a << endl;
	 //如果想要通过形参改变外部实参时,必须借助std::ref()函数
	thread t2(ThreadFunc1, ref(a));
	t2.join();
	cout << a << endl;
	// 地址的拷贝
	thread t3(ThreadFunc2, &a);
	t3.join();
	cout << a << endl;
	return 0;

 而如果我们使用右值引用,本来想的是因为是移动构造,在线程函数中改变的值也会改变主线程的参数,但实验结果是仍然不变。

void ThreadFunc(int&& a)
{
	a += 10;
}
int main()
{
	int a = 10;
	std::thread t1(ThreadFunc,10);
	cout << a << endl;  //10
	t1.join();
	return 0;
}

结果输出仍然是10,其原因是传入线程的不是a本身的地址,而是临时拷贝。

1.2 :如果是类成员函数作为线程参数时,必须将this作为线程函数参数。


class Test
{
public:
	void thread()
	{
		cout << "This is Test::show()" << endl;
	}
};
//注意:如果是类成员函数作为线程参数时,必须将this作为线程函数参数。
void main()
{
	Test t;

	thread th(&Test::thread, &t);   //this->show
	th.join();
	//th.join();
}

2、mutex锁

 如果我们需要保护一段代码的安全,可以对这段代码进行加锁。当然效率也会降低。

#include <thread>
#include <mutex>
int number = 0;
mutex mt;
int ThreadProc1()
{
	for (int i = 0; i < 100; i++)
	{
		mt.lock();
		++number;
		cout << "thread 1 :" << number << endl;
		mt.unlock();
	}
	return 0;
}
int ThreadProc2()
{
	for (int i = 0; i < 100; i++)
	{
		mt.lock();
		--number;
		cout << "thread 2 :" << number << endl;
		mt.unlock();
	}
	return 0;
}
int main()
{
	thread t1(ThreadProc1);
	thread t2(ThreadProc2);
	t1.join();
	t2.join();
	cout << "number:" << number << endl;
	system("pause");
	return 0;
}

3、原子操作

 由于加锁会极大的增加运用的开销,所以c++11增加了原子操作,可以理解为直接定义一个加锁的临界资源, 而在c++中原子操作就是这样的一种『小到不可分割的』操作。要使用原子操作我们需要引用c++11的一个新的头文件<atomic>.。
 增加了一个头文件,并且将 long sum = 0L; 修改成了 std::atomic_long sum {0L}; 注意不要写成std::atomic_long sum = 0L的形式,因为long类型是不可以隐式转换为std::atomic_long类型的。

#include <iostream>
using namespace std;
#include <thread>
#include <atomic>
atomic_long sum{ 0 };
void fun(size_t num)
{
	for (size_t i = 0; i < num; ++i)
		sum++; // 原子操作
}
int main()
{
	cout << "Before joining, sum = " << sum << std::endl;
	thread t1(fun, 1000000);
	thread t2(fun, 1000000);
	t1.join();
	t2.join();
	cout << "After joining, sum = " << sum << std::endl;
	return 0;
}

atomic模板中还实现了操作符的重载(atomic头文件),因此你可以像使用内置的数据类型那样使用原子数据类型(c++保证这些操作是原子操作)。对应于内置的数据类型,原子数据类型都有一份对应的类型,归纳出来如下:
 在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值