C++多线程1(复习向笔记)

创建线程以及相关函数

当用thread类创建线程对象绑定函数后,该线程在主线程执行时就已经自动开始执行了,join起到阻塞主线程的作用

cpp 复制代码
#include <iostream>
#include <thread>
#include <string>
using namespace std;
//测试函数
void printString(string str)
{
	cout << str << endl;
}
int main()
{
	//创建线程,需要包括thread头文件,构造函数后续参数里可选,放入线程绑定函数的参数
	thread thread1(printString,"Hello World!");
	//joinable函数,有些线程不一定能用join或者detach,joinable返回一个布尔值,true时使用join增加严谨性
	bool flag = thread1.joinable();
	if (flag)
		thread1.join();//主程序等待线程执行完毕,不加join可能会导致线程绑定的函数没执行完,主程序就结束了

	//分离主线程和子线程,会可能导致子线程还未执行完毕,主线程就结束了
	/*thread1.detach();*/
	return 0;
}

使用detach输出结果,一般使用较少

更直观的join和joinable的例子

cpp 复制代码
#include <iostream>
#include <thread>
using namespace std;

void display()
{
	for (int i = 0; i < 100; i++)
		cout << i << " ";
	cout << endl << "子线程结束" << endl;
}

int main()
{
	thread thread1(display);
	bool flag = thread1.joinable();

	if (flag)
		thread1.join();
	cout << "主线程结束" << endl;
	return 0;
}

输出
不难发现,主线程阻塞在了join中,等待子线程执行完毕,再往下执行

常见问题以及解决方法

一般注意变量的声明周期即可

绑定函数的参数为引用

使用ref()函数

cpp 复制代码
#include <iostream>
#include <thread>
using namespace std;

void test01(int& a)
{
	a += 1;
}
int main()
{
	int a = 11;
	//绑定函数时参数,传递引用时需要用std下的ref函数将变量转换为引用类型,否则thread无法判断为引用类型
	thread thread1(test01, ref(a));
	if (thread1.joinable())
		thread1.join();
	cout << a << endl;
	return 0;
}


错误例子,运行结果报错,也有可能不报错
错误原因,在本例中,a不在main中,函数执行完后内存会释放掉,由于是线程模式,test01和test02的执行速度不一致,有可能在test02执行完后,test01还没有执行完,所以导致了空指针异常!
解决方法:将a变成局部变量

cpp 复制代码
#include <iostream>
#include <thread>
using namespace std;
thread thread1;
void test01(int& a)
{
	a += 1;
}

void test02()
{
	int a = 11;
	//绑定函数时参数,传递引用时需要用std下的ref函数将变量转换为引用类型,否则thread无法判断为引用类型
	thread1 = thread(test01, ref(a));
}
int main()
{
	
	test02();
	if (thread1.joinable())
		thread1.join();
	return 0;
}

指针提前释放

cpp 复制代码
//指针案例
void test03(int* a)
{
	cout << "*a = " << * a << endl;
}
int main()
{
	int* p = new int(1);
	thread thread1(test03,p);
	//在join之前手动释放了指针,指针指向的空间值变成了随机值
	delete p;
	if (thread1.joinable())
		thread1.join();
	return 0;
}

类成员函数作为线程函数,但对象被提前释放

其实跟指针的报错差不多,注意在join()之前不要释放对象即可
智能指针可以减少在join()之前手动释放的问题,它会自动调用析构函数自己销毁对象

整体源代码

cpp 复制代码
#include <iostream>
#include <thread>
#include <memory>
using namespace std;
//引用案例
void test01(int& a)
{
	a += 1;
}
//指针案例
void test03(int* a)
{
	cout << "*a = " << * a << endl;
}
//类案例
class A {
public:
	void func()
	{
		cout << "类成员函数作为线程函数" << endl;
	}
};
int main()
{
	A a;
	//类成员函数作为线程函数时,需要用&来制定成员函数的地址,同时还需要传递指针/引用
	thread thread1(&A::func,&a);
	thread1.join();
	//智能指针,已经是指针了
	shared_ptr<A> b = make_shared<A>();
	//直接传入值即可
	thread thread2(&A::func, b);
	thread2.join();
	return 0;
}

互斥锁解决变量访问互质问题

学过OS这部分就很容易理解了

只是C++具体实现罢了,具体看代码
线程安全:如果多线程程序每一次运行的结果跟单线程程序运行的结果是一样的,那么就称这个线程是安全的

cpp 复制代码
#include <iostream>
#include <thread>
#include<mutex>
using namespace std;
mutex mtx;
int a = 0;
void add()
{
	for (int i = 0; i < 1000; i++)
	{
		//上锁相当于P(mutex)
		mtx.lock();
		a++;
		//解锁相当于V(mutex)
		mtx.unlock();
	}
		
}
int main()
{
	//创建两个线程
	thread thread1(add);
	thread thread2(add);

	thread1.join();
	thread2.join();
	//输出共享变量
	cout << a << endl;
	return 0;
}
相关推荐
芊寻(嵌入式)5 分钟前
C转C++学习笔记--基础知识摘录总结
开发语言·c++·笔记·学习
獨枭7 分钟前
C++ 项目中使用 .dll 和 .def 文件的操作指南
c++
霁月风10 分钟前
设计模式——观察者模式
c++·观察者模式·设计模式
橘色的喵11 分钟前
C++编程:避免因编译优化引发的多线程死锁问题
c++·多线程·memory·死锁·内存屏障·内存栅栏·memory barrier
一颗松鼠14 分钟前
JavaScript 闭包是什么?简单到看完就理解!
开发语言·前端·javascript·ecmascript
有梦想的咸鱼_16 分钟前
go实现并发安全hashtable 拉链法
开发语言·golang·哈希算法
海阔天空_201321 分钟前
Python pyautogui库:自动化操作的强大工具
运维·开发语言·python·青少年编程·自动化
天下皆白_唯我独黑29 分钟前
php 使用qrcode制作二维码图片
开发语言·php
夜雨翦春韭32 分钟前
Java中的动态代理
java·开发语言·aop·动态代理
准橙考典33 分钟前
怎么能更好的通过驾考呢?
人工智能·笔记·自动驾驶·汽车·学习方法