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

news2024/10/28 7:57:12

目录

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.coloradmin.cn/o/2225295.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

jenkins ssh 免密报错Host key verification failed.

jenkins 发布项目&#xff0c;ssh连接远程服务器时报错&#xff1a;Host key verification failed. 解决&#xff1a; 原因是生成的sshkey不是用的jenkins用户&#xff0c;所以切换用户到&#xff1a;jenkins重新生成sshkey su jenkins ssh-keygen -t rsa ssh-copy-id -i ~/…

Linux: Shell编程入门

Shell 编程入门 1 ) Shell 概念 shell 是 在英语中 壳, 外壳的意思可以把它想象成嵌入在linux这样的操作系统里面的一个微型的编程语言不像C语言, C 或 Java 等编程语言那么完整&#xff0c;它可以帮我们完成很多自动化任务例如保存数据监测系统的负载等等&#xff0c;我们同样…

MATLAB生物细胞瞬态滞后随机建模定量分析

&#x1f3af;要点 基于随机动态行为受化学主方程控制&#xff0c;定量分析单细胞瞬态效应。确定性常微分方程描述双稳态和滞后现象。通过随机性偏微分方程描述出暂时性滞后会逐渐达到平稳状态&#xff0c;并利用熵方法或截断方法计算平衡收敛速度的估计值。随机定量分析模型使…

什么是字节序、大小端、高低字节、高低地址?

目录 1. 什么是字节序&#xff08;Endianness&#xff09;&#xff1f; 2. 什么是大小端&#xff08;Big-Endians and Little-Endian&#xff09;&#xff1f; 3. 什么时候需要用到大小端的概念&#xff1f; 4. 如何确认系统的大小端模式&#xff1f; 5. 什么是大小端定义…

[LVGL] MessageBox

该例子用lvgl9 来测试&#xff0c;对话框从底部升上来。当点击关闭或者确认按键时&#xff0c;会向绑定对象发送按键事件&#xff0c;参数 100/101. /*** file lv_demo_test.c**/#include "stdio.h" #include "stdlib.h" #include "lvgl.h"#ifde…

985研一,转嵌入式好还是后端开发好?

有个老铁问&#xff0c;985研一&#xff0c;转嵌入式好还是后端开发好&#xff1f; 我认为&#xff0c;这学历&#xff0c;两个随便挑&#xff0c;我说的&#xff0c;从趋势来看&#xff0c;更建议嵌入式&#xff0c;走供应链上游&#xff0c;芯片原厂、新能源车企、军工或者搞…

IDEA自动生成时序图插件-SequenceDiagram

目录 前言介绍安装在线安装离线安装 使用基本使用使用技巧 知识扩展为什么要画时序图&#xff1f;为什么要使用SequenceDiagram插件&#xff1f; 前言 工欲善其事&#xff0c;必先利其器&#xff0c;用对工具&#xff0c;事半功倍。我向大家介绍一款卓越的插件——Sequence Dia…

衡石分析平台系统分析人员手册-展示类控件创建富文本攻略

富文本​ 富文本控件是一种常见的控件&#xff0c;可用来展示文本信息、用户属性信息&#xff0c;在数据分析中起到辅助分析的功能。 富文本常见的使用场景有&#xff1a; 仅展示纯文本信息。在富文本中展示数据集字段、指标、参数等信息。使用富文本展示用户属性相关信息。在…

H3m-Blog

H3m-Blog 一、项目介绍 1.1 项目介绍 一个基于SpringBoot和Vue3的博客系统&#xff0c;博客名称来源于陈奕迅于2009年发布的粤语专辑《H3M》 1.2 技术架构 主要技术栈&#xff1a; SpringBoot2 Vue3 MySQL8.0 1.3 主要功能 内容丰富&#xff0c;尽情体验~ 二、快速开始…

Win11安装基于WSL2的Ubuntu

1. 概述 趁着还没有完全忘记&#xff0c;详细记录一下在Win11下安装基于WSL2的Ubuntu的详细过程。不得不说WSL2现在被微软开发的比较强大了&#xff0c;还是很值得安装和使用的&#xff0c;笔者就通过WSL2安装的Ubuntu成功搭建了ROS环境。 2. 详论 2.1 子系统安装 在Win11搜…

【HarmonyOS NEXT】使用 Navigation 对折叠屏设备页面进行分栏展示,优化 UI 交互

关键词&#xff1a;折叠屏、navigation、router、路由、分栏、UI 随着科技的发展&#xff0c;手机设备形态也由一面屏向多面屏进行发展&#xff0c;那么软件的UI适配也面临着问题&#xff0c;本篇文章主要解决大屏设备的页面 UI 适配问题&#xff0c;如折叠屏&#xff0c;平板&…

MySql数据库中数据类型

本篇将介绍在 MySql 中的所有数据类型&#xff0c;其中主要分为四类&#xff1a;数值类型、文本和二进制类型、时间日期、String 类型。如下&#xff08;图片来源&#xff1a;MySQL数据库&#xff09;&#xff1a; 目录如下&#xff1a; 目录 数值类型 1. 整数类型 2. …

[MoeCTF 2022]endian

查看发现是64位文件&#xff0c;且看到了amd64-64-little&#xff08;这里是小端序&#xff09; 所以我们要反向输入 对于整数 0x12345678&#xff0c;在小端序存储下的内存布局为&#xff1a; 地址 内容 低地址 0x78 0x56 0x34 高地址 0x12 查看main函数&#xff0c…

Python4

4. 更多控制流工具 除了刚介绍的 while 语句&#xff0c;Python 还用了一些别的。我们将在本章中遇到它们。 4.1. if 语句 if elif else if x<0: x 0 print(Negative changed to zero) elif x0: print( zero) else: print(More) 4.2. for 语句 Pyth…

游戏服务器被攻击有办法防护吗

游戏服务器受到攻击时比较常见的。就算是刚上线的游戏&#xff0c;都会有被攻击的时候。游戏服务器受到攻击的原因以及解决方案有哪些呢&#xff1f; 游戏服务器被攻击的原因有哪些呢&#xff1f; 1、常见的攻击&#xff0c;大部分来自于同行之间的恶意竞争&#xff0c;你的游…

Rust 力扣 - 3. 无重复字符的最长子串

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 我们需要记录当前子串的开始下标&#xff0c;一个哈希表记录字符和遍历过程中最后一次出现的索引 遍历字符串&#xff0c;如果在当前字符在哈希表中有记录&#xff0c;并且索引下标大于当前子串的开始下标&…

Lesson12---queue

Lesson12—queue 本篇博客介绍了cqueue的介绍使用以及模拟实现 文章目录 Lesson12---queue前言一、queue的成员函数1 queue2.empty3.size4.front5.back6.push7.pop 二、相关题目三、模拟实现完整代码 四、deque&#xff08;双端队列&#xff09;总结 前言 queue的文档:https:…

go高并发之路——本地缓存

一、使用场景 试想一个场景&#xff0c;有一个配置服务系统&#xff0c;里面存储着各种各样的配置&#xff0c;比如直播间的直播信息、点赞、签到、红包、带货等等。这些配置信息有两个特点&#xff1a; 1、并发量可能会特别特别大&#xff0c;试想一下&#xff0c;一个几十万…

CANopen中错误帧的制造和观测

本文讲述如何制造和观察CANopen中的错误帧&#xff08;Error frame&#xff09;&#xff0c;以Linux下的“VCAN0”为CAN接口来做测试&#xff0c;所以事先要先创建vcan0 一 制造错误帧 首先安装python CAN&#xff0c; pip3 install python-can然后参考python can的文档编写如…

Android View

前面我们了解了Android四大组件的工作流程&#xff0c;Android中还存在一个和四大组件地位相同的概念&#xff1a;View&#xff0c;用于向用户页面展示内容。我们经常使用的TextView、Button、ImageView控件等都继承于它&#xff0c;也会自定义View实现自定义效果。View类源码内…