
多线程编程
多线程编程(基于C++)
目录
什么是多线程
首先我们需要了解什么是线程。
- 线程(Thread)是操作系统能够进行调度的最小单位,它比进程更小,是进程的一个子集。线程是程序执行的最小单位,用于实现并发执行。
- 多线程指的是一个程序中包含两个或者两个以上的线程,多线程的提出是为提高代码的执行效率,这就好比工厂中的流水线,只有一条称为单线程,有多条流水线就称为多线程。
我这里还有更形象一点的例子。AI生成
- 线程比喻:餐厅里的服务员
想象你走进一家餐厅,餐厅就像一个程序,而餐厅里的服务员就像线程。
- 餐厅(进程)
- 餐厅是整个程序的运行环境,它有自己的资源,比如厨房(内存)、餐具(文件句柄)、服务员(线程)等。
- 餐厅的资源是有限的,但它可以同时为多个顾客服务。
- 服务员(线程)
- 服务员是餐厅里的线程,他们负责完成各种任务,比如点菜、送餐、清理桌子等。
- 每个服务员可以独立完成任务,但他们共享餐厅的资源(比如厨房和餐具)。
- 顾客(任务)
- 餐厅里的顾客代表程序中的任务。每个顾客有不同的需求,比如点不同的菜或者要求不同的服务。
- 一个服务员可以同时服务多个顾客,这就是线程的并发性。
线程的特点(用餐厅来比喻)
轻量级
- 服务员的加入和离开(线程的创建和销毁)相对容易,不需要重新装修餐厅(进程的资源)。
- 一个服务员(线程)的开销比重新开一家餐厅(进程)小得多。
并发性 - 多个服务员可以同时为不同的顾客服务,就像多个线程可以同时执行不同的任务。
- 这样可以提高餐厅的效率,尤其是在顾客很多的时候。
共享资源 - 所有的服务员共享餐厅的厨房、餐具等资源,就像线程共享进程的内存和文件句柄。
- 这使得服务员之间可以高效地协作,比如一个服务员可以去厨房取菜,另一个服务员可以去送菜。
独立性 - 每个服务员有自己的任务和职责,比如点菜、送餐等,就像线程有自己的执行栈和局部变量。
- 他们可以独立完成任务,但也可以协作完成复杂的任务。
线程的生命周期(用餐厅来比喻)
新建(New) - 一个新服务员被招聘进来,但还没有开始工作。他站在餐厅门口,等待分配任务。
就绪(Runnable) - 服务员已经准备好工作,等待餐厅经理(操作系统)的调度,比如分配一个顾客给他服务。
运行(Running) - 服务员正在为顾客服务,比如点菜、送餐等。
阻塞(Blocked) - 服务员因为某些原因暂时无法继续服务,比如等待厨房准备好菜,或者顾客还没决定点什么菜。
终止(Terminated) - 服务员完成了所有任务,或者被餐厅经理(操作系统)解雇了,不再工作。
总结
线程就像餐厅里的服务员,它们是程序(餐厅)中的独立执行单元,可以并发执行任务,共享资源,但又独立完成自己的工作。通过合理安排线程(服务员),可以大大提高程序(餐厅)的效率。
创建线程
首先需要额外引入这三个库
#include <thread>
#include <chrono>
#include <atomic>
简单解释一下库的作用:
(不能很好的理解的话可以去问问deepseek,它能给出更通俗的解释)
- thread
C++用于线程创建的库
- chrono
用于处理时间相关的功能,包括时间点、时间间隔、时钟等
- atomic
原子操作是指不可分割的操作。在多线程环境中,当多个线程同时访问和修改一个共享变量时,普通的读写操作可能会导致竞态条件(race condition),从而导致数据不一致。而原子操作可以确保在执行过程中不会被其他线程中断,从而保证操作的完整性。
通过类来创建一个线程
class Mythod
{
public:
Mythod()
{
state = 1;
}
// 运行线程的函数
void run()
{
while (state)
{
this_thread::sleep_for(chrono::milliseconds(500));
cout << "Hello!" << endl;
}
}
// 停止线程的函数
void stop()
{
state = 0;
}
private:
atomic<bool> state;// 原子操作
};
对于主程序
int main()
{
Mythod mythod;
thread thread(&Mythod::run, ref(mythod));
this_thread::sleep_for(chrono::seconds(3));
mythod.stop(); // 调用停止线程的方法
thread.join(); // 用于等待线程结束
cout << "线程任务执行结束" << endl;
return 0;
}
线程管理
xx.join() // 用于等待线程完成执行。如果不调用 join() 或 detach() 而直接销毁线程对象,会导致程序崩溃。
xx.detach() // 将线程与主线程分离,线程在后台独立运行,主线程不再等待它。但是它必须在主线程结束之前结束,否则也会导致程序崩溃。
线程的传参
无参数
<thread_name>(func);
值传递
<thread_name>(func,arg1,arg2,...);
引用传递
<thread_name>(func,std::ref(arg1),...) // 涉及引用就用std::ref()
线程的同步与互斥
同步与互斥这两个概念主要用于控制多个线程对共享资源(同一个变量)的访问,以避免数据竞争、死锁等问题
互斥量(Mutex)
互斥量是一种同步原语,用于防止多个线程同时访问共享资源。当一个线程需要访问共享资源时,它首先需要锁定(lock)互斥量。如果互斥量已经被其他线程锁定,那么请求锁定的线程将被阻塞,直到互斥量被解锁(unlock)。
std::mutex mtx;
mtx.lock(); // 锁定互斥锁
// 访问共享资源
mtx.unlock(); // 释放互斥锁
std::lock_guard
和 std::unique_lock
:自动管理锁的获取和释放。
std::lock_guard<std::mutex> lock(mtx); // 自动锁定和解锁
// 访问共享资源
这只是我的一点点肤浅的学习,要更加深入了解多线程编程还得去阅读更多的文档以及去多多实践
参考资料
https://www.runoob.com/cplusplus/cpp-multithreading.html
https://blog.csdn.net/yahid/article/details/125921786
https://blog.csdn.net/luoweifu/article/details/46835437