当前位置: 首页 > article >正文

C++多线程编程(1):线程的创建方式

文章首发于我的个人博客:欢迎大佬们来逛逛

文章目录

  • 进行与线程
  • C++中如何实现多线程
  • 创建线程的多种方式
    • 无参函数
    • lambda表达式
    • 常成员函数
    • not常成员引用函数
    • 智能指针
    • 仿函数
    • 类的普通成员函数
    • 综合测试

进行与线程

多线程是指多个线程并发执行的过程。

进程与线程的关系:

  • 进程是一个独立运行的应用程序
  • 线程是指进程内独立执行的一个单元,一个进程中可能有多个线程。

C++中如何实现多线程

使用 #include <thread> 头文件,里面定义了很多的线程函数。

其中,使用 thread 创建一个线程.

比如我有两个函数,分别是:

  • print1是主线程。
  • 下面四个函数分别是子线程。
void print1() {
	std::cout << "主线程\n";
}
void printtttt1() {
	Sleep(2000);
	std::cout << "子线程1\n";
}
void printtttt2() {
	Sleep(2000);
	std::cout << "子线程2\n";
}
void printtttt3() {
	Sleep(2000);
	std::cout << "子线程3\n";
}

现在我们想让他们四个同时执行?如何操作。

  1. 首先来创建线程对象:

thread 用作线程对象类型,然后传递一个函数指针(以模板形式)给到这个对象,则这个对象就是一个线程对象

我们将这三个子线程分别为三个不同的线程对象。print1为主线程。

std::thread t1(printtttt1);
std::thread t2(printtttt2);
std::thread t3(printtttt3);

创建完成后我们使用 join将子线程添加到主线程中:

t1.join(); 
t2.join();
t3.join();

然后运行,就会发现,三个子线程和主线程同时结束,说明多线程有效。

int main() {
	std::thread t1(printtttt1);
	std::thread t2(printtttt2);
	std::thread t3(printtttt3);
	//t.join(); //子线程加入主线程
	//detach: 子线程和主线程各自玩个的,等待主线程执行完毕
	t1.join(); 
	t2.join();
	t3.join();
	print1();
	return 0;
}

观察到细节:

  • 三个子线程都有一个等待两秒的功能,如果不是多线程,则很容易想到单纯的运行这四个函数可能需要6秒多才完成
  • 但是他们四个是同时完成的,即只用了两秒
  • 并且我们没有限制线程之间的执行顺序,因此他们的顺序是任意的。

在这里插入图片描述


join函数就是将子线程加入到主线程,然后和主线程一起执行完毕。

还有个 detach函数:

  • detach: 子线程和主线程各玩各的的,等待主线程执行完毕则停止。
t1.detach();
t2.detach();
t3.detach();
print1();

则会出现什么?

  • 程序立刻结束,我管你子线程执行了没有,只要我的主线程结束了,则程序就结束。 因此程序直接执行主线程函数,而不会执行三个子线程。

在这里插入图片描述


joinable:对线程是否可以join和detach操作进行判断。即一个线程只能进行一次join或者detach操作,如果你写了很多的代码,明明已经join过一次了,但是忘记了,因此又join了一次,这时就会 报错!!!

该函数在可以 join 或 detach 的时候返回true,否则返回false。

因此常见的可以避免错误的方式:

if (t1.joinable()) {
		t1.join(); //t1.detach()
	}

创建线程的多种方式

thread类型的构造函数是怎样的呢? 它可以构造什么样的线程函数对象呢?

template <class _Fn, class... _Args,   .......... >
explicit thread(_Fn&& _Fx, _Args&&... _Ax) {
        _Start(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...);
}
  • Fn:接受一个函数指针。
  • Args:接受函数的参数。

无参函数

这是最简单的创建线程的方式:

// 1. 传递无参void函数
void print() {
	std::cout << "子线程: " << "传递无参void函数\n";
}
void create1() {
	std::thread t1(print);
	t1.join(); //加入主线程
}

lambda表达式

// 2. 传递lambda表达式
void create2() {
	std::thread t1([]() {
		std::cout << "lambda表达式\n";
		});

	if (t1.joinable()) {
		t1.join();
	}
}

常成员函数

  • 普通内置类型:int
  • 常引用:const string& ; const int&
  • 常指针:const int*
  • 都可以直接传递
// 3. 传递有参函数
// 3.1  普通参数
void print2(int num, const std::string& name, const int age, const int& yina, const int* cp) {
	std::cout << "num: " << num << " name: " << name << " age: " << age << " yina: "
		<< yina << " cp: " << *cp << '\n';
}
void create3() {
	int num = 10, age = 20, yina = 999, cpnum = 50;
	std::thread t1(print2, num, "你好", age, yina, &cpnum);
	if (t1.joinable()) {
		t1.join();
	}
}

not常成员引用函数

  • not常引用: 如果是**不带const**的引用类型,则必须使用 std::ref 修饰,否则会报错:
  • not常指针:不会报错。
// 必须加以 ref 修饰;否则就传递const的引用
void print3(int& num) {
	std::cout << "引用 num: " << num << '\n';
}
void create4() {
	int num = 10;
	std::thread t1(print3, std::ref(num));
	if (t1.joinable()) {
		t1.join();
	}
}

//不会报错,const和非const的指针都不会报错,传递地址即可
void print4(int* num) {
	std::cout << "指针 num: " << *num << '\n';
}
void create5() {
	int num = 10;
	std::thread t1(print4, &num);
	if (t1.joinable()) {
		t1.join();
	}
}

智能指针

传递智能指针 unique_ptr 需要加 move移动,因为unique_ptr只允许存在一份,但是移动后本地将消失。

void print5(std::unique_ptr<int> ptr) {
	std::cout << "智能指针: " << *ptr.get() << '\n';
}
void create6() {
	std::unique_ptr<int> pointer(new int{ 100 });
	std::thread t1(print5, std::move(pointer));
	if (t1.joinable()) {
		t1.join();
	}

	//null
	std::cout << "移动之后,智能指针: " << pointer.get() << '\n';
}

仿函数

直接传递即可。

class Foo {
public:
	Foo() {}
	void operator()() {
		std::cout << "仿函数\n";
	}
};
void create7() {
	Foo f = Foo();
	//1. 仿函数对象
	std::thread t1(f);
	if (t1.joinable()) {
		t1.join();
	}

	//2. 匿名函数对象
	std::thread t2((Foo()));
	if (t2.joinable()) {
		t2.join();
	}
}

类的普通成员函数

函数指针的形式,先传递类名所对应的**函数地址,然后再传递类对象**。

class Aoo {
public:
	Aoo() {}
	void test() {
		std::cout << "普通成员函数\n";
	}
};
void create8() {
	Aoo a = Aoo();
	std::thread t1(&Aoo::test,a);
	if (t1.joinable()) {
		t1.join();
	}
}

综合测试

int main() {
	create1();
	create2();
	create3();
	create4();
	create5();
	create6();
	create7();
	create8();
	return 0;
}

在这里插入图片描述



http://www.kler.cn/news/134129.html

相关文章:

  • Node.js之TCP(net)
  • python 词云 wordcloud使用paddle模式 庆余年人物分析--不是特别准,可以看着玩一玩
  • 基于深度学习的单帧图像超分辨率重建综述
  • Postman接收列表、数组参数@RequestParam List<String> ids
  • C练习题_13
  • Zabbix实现故障自愈
  • rabbitmq默认交换机锁绑定的routingkey-待研究
  • ICASSP2023年SPGC多语言AD检测的论文总结
  • 算法设计与分析复习--贪心(二)
  • 开源更安全? yum源配置/rpm 什么是SSH?
  • yolov5模型代码怎么修改
  • Cesium+Vue:地形开挖
  • Ps:变换
  • 应用协议安全:Rsync-common 未授权访问.
  • Vue3+Vite实现工程化,事件绑定以及修饰符
  • C# GC机制
  • aspose.cells java合并多个excel
  • SpringCloud微服务注册中心:Nacos介绍,微服务注册,Ribbon通信,Ribbon负载均衡,Nacos配置管理详细介绍
  • 【算法】树形DP③ 监控二叉树 ⭐(二叉树染色二叉树灯饰)!
  • 设计模式-行为型模式-策略模式
  • Spring Cloud学习(十)【Elasticsearch搜索功能 分布式搜索引擎02】
  • 目标检测YOLO系列从入门到精通技术详解100篇-【目标检测】三维重建
  • useEffect 和useLayoutEffect 的区别
  • CSS 文本属性篇
  • VMware——WindowServer2012R2环境mysql5.7.14解压版安装主从复制(图解版)
  • app使用
  • Stable Diffusion - StableDiffusion WebUI 软件升级与扩展兼容
  • 李宏毅2023机器学习作业HW05解析和代码分享
  • install YAPI MongoDB 备份mongo 安装yapi插件cross-request 笔记
  • 建立跨层全栈的区块链安全保障系统-应用层,系统层,设施层