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

【c++高级篇】--多任务编程/多线程(Thread)

目录

1.进程和线程的概念:

1.1 进程(Process):

1.2线程(Thread):

1.3 对比总结:

2.多线程编程:

2.1 基于线程的多任务处理(Thread):

2.1.1. mbed.h 中的线程类

2.2 注意事项:(死锁)

2.2.1. 进程死锁

2.2.2 线程死锁

2.2.3 避免死锁(Thread):


1.进程和线程的概念:

进程和线程是操作系统中两个重要的概念,它们之间存在着密切的关系,但有不同的特性和用途。

1.1 进程(Process):

  • 操作系统中资源分配的最小单位,每个进程拥有独立的内存空间、系统资源(文件描述符、网络连接等)和生命周期。进程之间相对独立,无法直接共享彼此的内存空间。

进程虽然无法直接共享彼此内存空间 ,但是操作系统提供了一些机制,使得进程之间可以共享或交换部分资源,但这些共享和通信需要通过特殊的手段实现,而不是像线程那样直接访问共享的内存空间。

 进程间资源共享的常用方式:

  1. 管道(Pipe):允许一个进程将数据写入管道,另一个进程从管道中读取,用于单向通信。

  2. 消息队列(Message Queue):操作系统提供的一种数据结构,用于在多个进程之间发送和接收消息,实现异步通信。

  3. 共享内存(Shared Memory):一种特殊的内存区域,多个进程可以映射到这个区域来进行数据共享。共享内存速度快,但需要借助信号量等同步机制,避免数据竞争。

  4. 信号量(Semaphore):用来控制对共享资源的访问,防止多个进程同时访问导致冲突。

  5. 套接字(Socket):尤其用于分布式系统中,通过网络通信实现跨进程的数据共享和传递。

1.2线程(Thread):

  • 是操作系统中CPU调度的最小单位。线程共享同一进程的内存空间和系统资源,可以认为是进程中的“轻量级”执行单元。同一进程中的线程之间能够共享全局变量和数据,通讯成本低。

1.3 对比总结:


2.多线程编程:

多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。在一般情况下,有两种类型的多任务处理:基于进程和基于线程。

  1. 基于进程的多任务处理:每个进程都有自己的内存空间和系统资源,进程之间相对独立,切换开销较大,但安全性高。每个进程可以运行不同的程序,相互之间的通信通常需要更复杂的机制,比如进程间通信(IPC)。

  2. 基于线程的多任务处理:线程是进程内的一个执行单元,多个线程共享同一进程的内存空间和资源,因此切换开销相对较小,适合需要频繁进行上下文切换的任务。由于共享内存,线程间的通信更为高效,但也带来了同步和安全性的问题。

2.1 基于线程的多任务处理(Thread):

以嵌入式单片为例,在mbed OS中,mbed.h库提供了对线程的支持,使得在嵌入式系统中可以方便地实现多线程。

2.1.1. mbed.h 中的线程类

mbed.h库中,线程主要通过 Thread 类来管理和操作。该类提供了创建、启动、暂停、恢复和终止线程的基本操作方法。

常用的 Thread 类方法有:

  • Thread::start(callback):启动线程,执行指定的回调函数。
  • Thread::join():阻塞当前线程,直到其被调用线程完成。(顺序执行),调用 join() 后,主线程会被阻塞,直到子线程执行完毕。
  • Thread::terminate():强制终止线程的执行。
  • Thread::yield():让出CPU,使其他线程有机会运行。
  • Thread::sleep_for(milliseconds):使线程在指定的毫秒数内休眠。
#include "mbed.h"// 定义LED灯
DigitalOut led1(LED1);
DigitalOut led2(LED2);// 定义线程
Thread thread1;
Thread thread2;// 线程函数1 - 控制LED1闪烁
void led1_thread() {while (true) {led1 = !led1;  // 切换LED1的状态ThisThread::sleep_for(500ms);  // 每500ms切换一次}
}// 线程函数2 - 控制LED2闪烁
void led2_thread() {while (true) {led2 = !led2;  // 切换LED2的状态ThisThread::sleep_for(1000ms);  // 每1000ms切换一次}
}int main() {// 启动线程thread1.start(led1_thread);thread2.start(led2_thread);// 主线程可以执行其他任务,或等待子线程结束thread1.join();thread2.join();
}

2.2 注意事项:(死锁)

进程死锁线程死锁都涉及资源的相互等待,导致无法继续执行。

2.2.1. 进程死锁

进程死锁发生在两个或多个进程之间。当进程A持有资源1并等待资源2,而进程B持有资源2并等待资源1时,两个进程将永远处于等待状态,无法继续执行。

特征

  • 互斥:资源只能被一个进程占用。(不同同时访问)

  • 持有并等待:进程在持有至少一个资源的情况下,申请其他资源。

  • 不剥夺:已经分配给进程的资源在其完成之前不能被强制剥夺。

  • 循环等待:存在一种进程资源的循环等待关系。

示例

进程A持有资源R1,并请求资源R2。 进程B持有资源R2,并请求资源R1。

2.2.2 线程死锁

定义

在多线程程序中,线程死锁通常发生在多个线程尝试以不同的顺序获取多个锁时。例如,线程1持有锁A并等待锁B,而线程2持有锁B并等待锁A,这将导致两个线程永远处于等待状态。

特征

  • 与进程死锁相似,同样具有互斥、持有并等待、不剥夺和循环等待的特征。

  • 线程死锁通常更复杂,因为线程之间的锁依赖关系可能更难以追踪

2.2.3 避免死锁(Thread):

同步和锁

同步指的是在多个线程之间协调它们的执行顺序,以确保某些操作以特定的顺序完成,防止出现竞态条件(race condition)。同步的目的在于确保共享资源的安全访问。

方法:同步可以通过多种方式实现,包括:

  • 互斥锁(Mutex):保证在某一时刻只有一个线程可以访问某个资源。
  • 信号量(Semaphore):控制同时访问某个资源的线程数量。
  • 条件变量(Condition Variable):使线程在某些条件不满足时阻塞,直到条件满足。
  • 读写锁(Read-Write Lock):允许多个线程同时读数据,但在写数据时需要独占访问。

互斥锁(Mutex):

#include "mbed.h"Mutex mutex;  // 创建一个互斥锁
int sharedResource = 0;  // 共享资源void threadTask() {for (int i = 0; i < 5; i++) {mutex.lock();  // 加锁,开始访问共享资源sharedResource++;  // 修改共享资源printf("线程 %d 修改共享资源: %d\n", ThisThread::get_id(), sharedResource);mutex.unlock();  // 解锁,释放共享资源ThisThread::sleep_for(500ms);}
}int main() {Thread thread1;Thread thread2;thread1.start(threadTask);thread2.start(threadTask);thread1.join();thread2.join();printf("最终共享资源值: %d\n", sharedResource);return 0;
}
  • 互斥锁(mutex)确保在同一时刻只有一个线程可以访问和修改sharedResource,避免了数据竞争。
  • 每个线程在修改共享资源前调用mutex.lock(),完成后调用mutex.unlock(),以确保资源安全。

读写锁(Read-Write Lock)

#include "mbed.h"RWLock rwLock;  // 创建读写锁
int sharedData = 0;  // 共享数据// 读线程
void readTask() {for (int i = 0; i < 5; i++) {rwLock.lock_read();  // 获取读锁printf("读线程 %d 读取共享数据: %d\n", ThisThread::get_id(), sharedData);rwLock.unlock_read();  // 释放读锁ThisThread::sleep_for(500ms);}
}// 写线程
void writeTask() {for (int i = 0; i < 3; i++) {rwLock.lock_write();  // 获取写锁sharedData++;  // 更新共享数据printf("写线程 %d 更新共享数据: %d\n", ThisThread::get_id(), sharedData);rwLock.unlock_write();  // 释放写锁ThisThread::sleep_for(1000ms);}
}int main() {Thread readThreads[5];Thread writeThread;// 启动多个读线程for (int i = 0; i < 5; i++) {readThreads[i].start(readTask);}// 启动一个写线程writeThread.start(writeTask);// 等待所有线程完成for (int i = 0; i < 5; i++) {readThreads[i].join();}writeThread.join();printf("所有任务完成\n");return 0;
}
  • 多个读线程readTask)同时读取共享数据sharedData。由于使用了读写锁,多个读线程可以并行地访问共享数据。

  • 写线程writeTask)在更新共享数据时,必须获取写锁,这会阻止其他线程(无论是读线程还是写线程)访问共享数据,直到写操作完成。


http://www.mrgr.cn/news/60091.html

相关文章:

  • 厂房区域人员进出人数统计-实施方案
  • 机器视觉-相机、镜头、光源(总结)
  • Linux LVS详解
  • Navicat 17 新功能 | 数据分析 data profiling
  • adb常见指令以及问题解决
  • Spring Cache-基于注解的缓存
  • spring-第十一章 注解开发
  • C语言 | Leetcode C语言题解之第516题最长回文子序列
  • 《贪婪算法实战:从理论到面试题的全面解析》
  • Qt example---40000 Chips
  • Multi-Agent应用领域及项目示例
  • C++ | Leetcode C++题解之第515题在每个树行中找最大值
  • 【Linux 25】网络套接字 socket 概念
  • 【skywalking 】选择Elasticsearch存储
  • 数据库如何保证主键唯一性
  • PyQt入门指南三十二 QStatusBar状态栏组件
  • 衡石分析平台系统分析人员手册-展示类控件创建富文本攻略
  • Java最全面试题->数据库/中间件->MongoDB面试题
  • 动态规划 - 背包问题 - 01背包
  • Java 标准流一口气讲完!-O-
  • web3.0 开发实践
  • orbslam安装
  • 复刻系列-原神 5.1 版本先行展示页
  • 温泉押金原路退回系统, 押金+手牌+电子押金单——未来之窗行业应用跨平台架构
  • 数据结构与算法分析:你真的理解查找算法吗——二分查找(代码详解)
  • 闯关leetcode——225. Implement Stack using Queues