CSSAPP稀里糊涂的读书笔记(十二)并发编程

本文探讨了进程、I/O多路复用和线程三种构造并发程序的方法。详细解释了线程的概念,包括线程的调度、上下文和共享变量。介绍了使用信号量同步线程的方法,避免死锁的策略,以及如何识别和解决线程不安全的函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本章主要研究三种基本的构造并发程序的方法:

  • 进程。每个逻辑控制流都是一个进程,由内核来调度和维护。因为进程有独立的虚拟地址空间,想要和其他流通信,控制流必须使用某种显示的进程间通信(IPC)机制。
  • I/O多路复用。应用程序在一个进程的上下文中显式地调度它们自己的逻辑流。逻辑流被模型化为状态机,数据到达文件描述符后,主程序显式地从一个状态转换到另一状态。因为程序是一个单独地进程,所以所有地流都共享同一个地址空间。
  • 线程。线程是运行在一个单一进程上下文中的逻辑流,由内核进行调度。你可以把线程看成是其他两种方式的混合体,像进程流一样由内核进行调度,而像I/O多路复用流一样共享同一个虚拟地址空间。
  1. 构造并发程序最简单的方法就是用进程,使用那些大家都很熟悉的函数,像fork、exec和waitpid。
    看下面图就是利用进程实现并发
    在这里插入图片描述
    在这里插入图片描述

  2. 基于I/O多路复用技术的基本思路是使用select 函数,要求内核挂起进程,只有在一个或多个I/O事件发生后,才将控制返回给应用程序。
    I/O多路复用可以用做并发事件驱动程序的基础,在事件驱动程序中,某些事件会导致流向前推进,一般的思路是将逻辑流模型化为状态机。不严格的说,一个状态机就是一组状态、输入事件和转移,其中转移是将状态和输入事件映射到状态。每个转移是将一个(输入状态,输入事件)对映射到一个输出状态。

  3. 线程就是运行在进程上下文中的逻辑流。线程由内核自动调度。每个线程都有它自己的线程上下文,包括一个唯一的整数线程ID(TID)、栈、栈指针、程序计数器、通用目的寄存器和条件码。所有运行在一个进程里的线程共享该进程的整个虚拟地址空间。
    多线程的执行模型在某些方面和多进程的执行模型是相似的。如图,每个进程开始生命周期时都是单一线程,这个线程称为主线程(main thread);在某一时刻,主线程创建一个对等线程(peer thread),从这个时间点开始,两个线程就并发的运行。
    在这里插入图片描述

  4. 我们说一个变量v是共享的,当且仅当它的一个实例被一个以上的线程引用。

  5. 使用信号量同步线程
    进度图将n 个并发线程的执行模型化为一条n 维笛卡尔空间中的轨迹线。
    如下指令顺序对应的轨迹线:
    H1、L1、U1、H2、L2、S1、T1、U2、S2、T2
    在这里插入图片描述
    对于线程i,操作共享变量cnt内容的指令(Li、Ui、Si)构成了一个(关于共享变量cnt的)临界区(critical section),这个临界区不应该和其他进程的临界区交替执行。换句话说,我们想要确保每个线程在执行它的临界区中的指令时,拥有对共享变量的互斥的访问。通常这种现象称为互斥。
    在进度图中,两个临界区的交集形成的状态空间区域称为不安全区。如下图
    在这里插入图片描述

Edsger Dijkstra,提出了一种经典的解决同步不同执行线程问题的方法,该方法基于一种叫做信号量(semaphore)的特殊类型变量的。信号量s 是具有非负整数值的全局变量,只能由两种特殊的操作来处理,这两种操作称为P和V:

  • P(s):如果s 是非零的,那么P 将s 减 1,并且立即返回。如果s 为零,那么就挂起这个线程,直到s变为非零,而一个V 操作会重启这个线程。在重启之后,P 操作将s 减 1,并将控制返回给调用者。
  • V(s):V 操作将s 加 1。如果有任何线程阻塞在 P 操作等待 s 变成非零,那么V 操作会重启这些线程中的一个,然后该线程将s 减 1,完成它的P 操作。

将每个共享变量(或者一组相关的共享变量)与一个信号量s (初始为 1)联系起来,然后用P(s)和V(s)操作将相应的临界区包围起来。
以这种方式来保护共享变量的信号量叫做二元信号量,因为它的值总是0或者1。以提供互斥为目的的二元信号量常常也称为互斥锁(mutex)。对应的P操作称为对互斥锁加锁;V操作称为对互斥锁解锁。
在这里插入图片描述

  1. 生产者-消费者问题和读者-写者问题,经典问题,建议直接看书,这里不说了。

  2. 一个函数被称为线程安全的,当且仅当被多个并发程序反复地调用时,它会一致产生正确的结果。因此,我们能定义出四个(不相交的)线程不安全函数类:
    第一类:不保护共享变量的函数。可以通过PV操作解决。
    第二类:保持跨越多个调用的状态的函数。需要修改调用程序中的代码。
    第三类:返回指向静态变量的指针的函数。一种方式是重写函数;另一种是使用加锁-复制技术。
    第四类:调用线程不安全函数的函数。用互斥锁保护调用位置和任何得到的共享数据。

  3. 死锁(deadlock),它指的是一组线程被阻塞了,等待一个永远也不会为真的条件。
    在这里插入图片描述

  • 使用P和V操作顺序不当,以至于两个信号量的禁止区域重叠。
  • 重叠的禁止区域引起了一组称为死锁区域的状态。
  • 死锁是一个相当困难的问题,因为它不总是可预测的。

互斥锁加锁顺序规则:给定所有互斥操作的一个全序,如果每个线程都是以一种顺序获得互斥锁并以相反的顺序释放,那么这个程序就是无死锁的。
在这里插入图片描述
先对s加锁,再对t加锁,得到了无死锁状态。

STM32(意法半导体)是一种广泛应用于嵌入式系统的微控制器平台。下载指的是将开发好的程序或固件烧录到STM32芯片内部的过程。 当使用STM32进行下载时,如果没有正确地设置或操作,就会出现稀里糊涂的情况。下载过程通常涉及到软件和硬件两个方面: 软件方面,首先需要选择合适的集成开发环境(IDE),例如Keil、IAR Embedded Workbench等,然后配置好开发环境中的下载工具。如果下载工具与芯片型号和连接方式不匹配,就会遇到下载失败或出现稀里糊涂的问题。 硬件方面,确保正确连接下载工具和目标STM32芯片,通常使用 JTAG 或 SWD 接口进行连接。如果连接错误或接触不良,也会导致下载失败或出现错误。 解决这种稀里糊涂问题的方法通常包括以下几步: 1. 检查软件设置:确认使用的IDE和下载工具版本与STM32芯片兼容;检查下载器和芯片连接设置是否正确。 2. 检查硬件连接:确保下载器正确连接到目标STM32芯片的JTAG或SWD接口上,并保持稳定的连接。 3. 引脚映射:检查芯片上的引脚映射是否正确,确保下载器与芯片之间的引脚对应正确。 4. 重启和复位:尝试对STM32芯片进行复位操作,或重新启动开发环境和下载工具。 如果以上方法都无效,可以尝试换用其他下载工具或IDE进行下载,或者咨询相关技术支持进行帮助。总之,稀里糊涂的STM32下载问题通常是由设置错误、连接问题或软硬件不兼容等原因引起的,通过仔细检查和调试可以解决。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值