C++11并发与多线程总结(一) --线程概念、创建及传参

文章目录

(一) 线程概念、创建及传参
(二) 独占互斥锁–mutex,lock_guardy与其他mutex
(三) unique_lock替换lock_guardy
(四) 单例模式(Singleton)下的线程安全问题
(五) window临界区
(六) condition_variable条件变量
(七) std::async异步任务与std::future< >
(八) packaged_task< >与promise< >
(九) 原子操作atomic<>简介

(1)基本概念
  1. 并发:一个程序同时执行多个程序任务。
  2. 进程:可执行程序运行,便创建了一个进程。
  3. 线程:代码的执行通路,每个进程都有一个主线程,且唯一,与进程一起产生、结束,不是越多越好,每个线程需要一个独立的堆栈空间,线程之间切换需切要保存很多中间的状态,切换过多降低程序的运行时间,最好不要超过两百个。

(2)创建线程

1.使用thread创建线程
2.创建后使用join,即与主线程汇合
3.创建后使用detacn,即与主线程分离,可能引发内存问题
4.joinable 是否可join,返回布尔值
5.类创建线程 (重载操作符)
6.lamda表达式创建线程


join()函数,让主线程等待子线程执行完成,相当于阻塞,join执行完毕,join()后面的主线程才会继续执行。这样可以有效防止主程序已经停止运行,而子程序没有运行结束,子程序不得不中断。
detach()函数,让子线程和主线程分开独自执行,无法控制,该子线程由c++运行时库进行控制管理,运行结束也是由c++运行时库进行释放。但是当主线程结束,该子线程也会随即中断结束。
  • 详情见代码块1

(3)线程传参

1.使用detach 传入 char* 有可能主函数执行完内存释放了产生异常,且必需加const
2.解决以上方法可以 使用构造临时对象 string("") 传入,若传入值则在线程内产生构造函数,但主程序已经结束释放
3.线程调用的函数参数需要加上 & 否则会调用三次构造
4.以上调用会产生构造函数与拷贝构造
5.std::this_thread::get_id() 可获取当前进程id
6.使用 mutable 定义 可修改const变量内容
7.std::ref() 可传入引用 不产生拷贝构造 也可使用&
8.调用类内部函数及传参
9.类似智能指针传引用需使用 std::move()


注意:在使用detach函数时,注意子线程和主线程之间不要使用“引用”方式来传递参数,否则某一个先结束,释放变量,会导致另一个线程出现bug。

  • 详情见代码块2


  • 测试代码块1
#include <iostream>
#include<thread>

using namespace std;


//子线程调用函数
void myprint();

class CPrint
{
public:
	CPrint() {};
	~CPrint() {};
	void operator()();		
};

void test1()//类创建线程
{
	CPrint my;
	thread myobj(my);
	myobj.join();
}

void test2()  //使用lambda表达式创建线程
{
	auto my = [] {
		for (auto i = 0; i < 10; i++)
		{
			cout << "mylambda:[" << i << "]" << endl;
		}
	};
	thread myobj(my);
	myobj.detach();
}

int main()
{
	//创建子线程,并开始执行函数
	thread myobj(myprint);

	//汇合,即堵塞
	//主线程等待子线程运行结束后才继续运行主线程,没join主线程提前结束会发生异常

	//if (myobj.joinable())
	//	myobj.join();

	//分离,非堵塞
	//主线程结束了仍执行,互不相关,被c++时库接管并结束后清理
	//detach后不可以再用join ,应用较少
	//若有引用主线程的值,主线程结束后会引发内存问题

	myobj.detach();
	if (myobj.joinable())
		cout << "能加入" << endl;
	else
		cout << "不能加入" << endl;
	for (int i = 0; i < 20; i++)
	{
		cout << "main_" << i << endl;
	}

	test2();
	test1();
	
}


void myprint()
{
	for (int i = 0; i < 20; i++)
	{
		cout << "子线程_" << i << endl;
	}
}


void CPrint::operator()()
{
	for (auto i = 0; i < 10; i++)
	{
		cout << "CPring::(" << i << ")" << endl;
	}
}


  • 测试代码块2
#include <iostream>
#include<thread>

using namespace std;

class CPrint
{
public:
	CPrint(int i) :id(i) { cout << "构造函数"<< this <<" id:" << id << "thread_id = " << this_thread::get_id() << endl; };
	CPrint(const CPrint& obj) { id = obj.id; cout << "拷贝构造 " << this << "id:" << id << "thread_id = " << this_thread::get_id() << endl; };
	~CPrint() { cout << "析构结束~ " << this << " thread_id = " << this_thread::get_id() << endl; };

	mutable int id; //可以在const下改变赋值

	void thread_work(int num) { id = 999; cout << "thread_work() " << this << "num:" << num << "thread_id = " << this_thread::get_id() << endl; }
};

void print(const CPrint& obj) //不加const会报错 除非传ref
{
	obj.id = 0;
	cout << "print() " << &obj << " id:" << obj.id << "thread_id = " << this_thread::get_id() << endl;
}

int main()
{
	CPrint obj1(666);
	thread mythread1(print, ref(obj1)); //构造和拷贝在主函数完成,使用ref则没有拷贝构造

	mythread1.join();
	cout << "main() obj1.id="<< obj1.id <<" thread_id = " << this_thread::get_id() << endl;//id已经改变

	cout << endl << endl;

	CPrint obj2(777);
	thread mythread2(&CPrint::thread_work, obj2,555); //调用类方法,也会产生拷贝构造

	mythread2.detach();
	cout << "main() obj1.id=" << obj2.id << " thread_id = " << this_thread::get_id() << endl; // id不改变

	cout << endl << endl;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值