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

嵌入式开发学习日志(linux系统编程--进程(4)——线程锁)Day30

扩:typedef三种用法(简化代码编写)

一、线程的控制——互斥和同步

(一)实例引入

1、示例:

 运行结果:

 两个线程都在运行,出现问题原因:资源竞争(对全局变量都进行了读写操作)

 2、扩展:

 

 

注:reg 寄存器,暂存;alu 逻辑计算单元

(1)读数据

 

(2)计算

(3)写数据

 (二)概念

1、互斥===》在多线程中对临界资源的排他性访问。

        临界资源:公共操作的东西(可为变量、设备);

        排他性访问:多线程在同一时刻只能有一个线程进行读或写操作。

2、互斥锁===》保证临界资源的访问控制(锁在系统中本质是结构体)

     使用步骤:     定义互斥锁 ——>初始化锁 ——>加锁 ——>解锁 ——>销毁

        (1)向系统申请锁mutex(在pcb块)约定1没锁,0锁了

        (2)初始化锁

        (3)尝试使用该资源,申请到后用的时候加锁(其它线程要使用该资源尝试解

                锁,解不开锁则进入休眠等待状态)

        (4)用完后解锁释放资源,系统通知,两个线程再次同时竞争资源

        (5)不需要互斥操作后销毁锁

(三)互斥锁相关函数

1、定义锁:pthread_mutex_t mutex;

(1)pthread_mutex_t :互斥锁类型        

(2) mutex:互斥锁变量(创建在pcb块中的东西) 也称内核对象(在内核中被定义

                        的)

2、初始化锁函数:pthread_mutex_init();

形式:int pthread_mutex_init(pthread_mutex_t *restrict mutex,
                  const pthread_mutexattr_t *restrict attr);

功能:将已经定义好的互斥锁初始化。

参数:mutex 要初始化的互斥锁

                   atrr  初始化的值,一般是NULL表示默认锁

返回值:成功 0;失败 非零

3、加锁:pthread_mutex_lock();

函数原型:

        int pthread_mutex_lock(pthread_mutex_t *mutex);

功能:

                用指定的互斥锁开始加锁代码

参数: 

        mutex 用来给代码加锁的互斥锁

返回值:

                成功 0;失败 非零

  加锁后的代码到解锁部分的代码属于原子操作(汇编角度来说多个语句一把走完)
  在加锁期间其他进程/线程都不能操作该部分代码
  如果该函数在执行的时候,mutex已经被其他部分使用则代码阻塞。

4、解锁:pthread_mutex_unlock(); 

函数原型:

        int pthread_mutex_unlock(pthread_mutex_t *mutex);

功能:

        将指定的互斥锁解锁。

        解锁之后代码不再排他访问,一般加锁解锁同时出现。

参数:

        用来解锁的互斥锁

返回值:

        成功 0;失败 非零

注:解锁之后代码不再排他访问,一般加锁解锁同时出现。

5、销毁:pthread_mutex_destroy();

函数原型:

         int pthread_mutex_destroy(pthread_mutex_t *mutex);

功能:

        使用互斥锁完毕后需要销毁互斥锁

参数:

        mutex 要销毁的互斥锁

返回值:

        成功  0; 失败  非零

6、非阻塞锁:pthread_mutex_trylock();

函数原型:

        int pthread_mutex_trylock(pthread_mutex_t *mutex);

功能:

        类似加锁函数效果,唯一区别就是不阻塞(稍后再试),非阻塞锁,CPU占有率较高

参数:

        mutex 用来加锁的互斥锁

返回值:

        成功 0(操作临界资源);非0(稍后再试)

        判断返回值状态(申请成功与否),外加while(1)

适用于有多个资源的情况,一般用阻塞锁居多。

 7、注意事项

(1)上互斥锁的地方不并发;

(2)被保护的临界资源要尽可能小(不要大递归、不要sleep)

8、示例

运行结果:

 

 9、练习

 

//模拟银行取钱#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>int WIN = 3;
pthread_mutex_t mutex;void *th(void *arg) {while (1) {pthread_mutex_lock(&mutex);if (WIN > 0) {--WIN;printf("get win\n");pthread_mutex_unlock(&mutex);int ret = rand() % 5 + 1;sleep(ret);pthread_mutex_lock(&mutex);++WIN;printf("2hao我取完钱了\n");pthread_mutex_unlock(&mutex);break;} else {pthread_mutex_unlock(&mutex);}}return NULL;
}int main(int argc, char **argv) {pthread_t tid[10] = {0};int i = 0;pthread_mutex_init(&mutex, NULL);for (i = 0; i < 10; ++i) {pthread_create(&tid[i], NULL, th, NULL);}int n = 0;for (n = 0; n < 10; ++n) {pthread_join(tid[n], NULL);}pthread_mutex_destroy(&mutex);return 0;
}

 二、线程的同步

(一)概念

1、定义:有一定先后顺序的对资源的排他性访问;(同步:互斥的基础上加上顺序)

     同步原因:互斥锁可以控制排他访问但没有次序。

2、信号量(本质是结构体):锁的一种

(1)信号量的定义(申请信号量与同步线程的个数相同)

(2)初始化信号量(信号量状态初值:1该走就走;0等待)

(3)执行体操作

(4)释放的是下一个信号量

 

3、信号量的PV操作

(1)P :申请资源——申请一个二值信号量(值不是1就是0)

(2)V :释放资源——释放一个二值信号量

4、信号量的分类

(1)无名信号量 ——>线程间通信

(2)有名信号量 ——>进程间通信

5、框架

        信号量的定义==》信号量的初始化==》信号量的pv操作==》信号量的销毁

(二)信号量相关函数

1、定义信号量:sem_t sem;

  sem_t                    sem;
   信号量的类型     信号量的变量

2、初始化信号量:sem_init();

函数原型:int sem_init(sem_t *sem, int pshared, unsigned int value);

功能:将已经定义好的信号量赋值;

参数:sem 要初始化的信号量

           pshared = 0 (表示线程间使用信号量);

           pshared != 0 (表示进程间使用信号量);

           value 信号量的初始值,一般无名信号量都是二值信号量(0 1) :

                       0 表示红灯,进程暂停阻塞;

                       1 表示绿灯,进程可以通过执行;

返回值:成功  0;失败  -1

3、申请信号量(P操作):sem_wait();

(1)函数原型int sem_wait(sem_t *sem);

(2)功能:判断当前sem信号量是否有资源可用。

                    如果sem有资源(==1),则申请该资源,程序继续运行;

                     如果sem没有资源(==0),则线程阻塞等待,一旦有资源则自动申请资源并

                        继续运行程序。

        :sem申请资源后会自动执行sem=sem-1;

(3)参数:sem 要判断的信号量资源;

(4)返回值:成功 0 ;失败 -1

4、释放信号量(V操作):sem_post();

(1)函数原型:int sem_post(sem_t *sem);

(2)功能:函数可以将指定的sem信号量资源释放;

                    并默认执行,sem = sem+1;

                    线程在该函数上不会阻塞。

(3)参数:sem 要释放资源的信号量;

(4)返回值:成功 0;失败 -1

5、销毁:sem_destroy();

(1)函数原型: int sem_destroy(sem_t *sem);

(2)功能:使用完毕将指定的信号量销毁;

(3)参数:sem要销毁的信号量;

(4)返回值:成功 0;失败  -1

6、信号量的两种用法示例

(1)同步(互斥+顺序):二值信号量

 

 

(2)互斥:计数信号量

资源个数不是1个时可使用

 

 三、死锁

1、死锁定义

        死锁是指多个进程或线程在竞争资源时,由于互相等待对方释放资源而陷入无限阻

塞的状态。(归根结底是逻辑错误导致的)

2、产生死锁的原因

(1) 因为系统资源不足。

(2) 进程运行推进的顺序不合适。

(3) 资源分配不当等。

如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。

 3、产生死锁的四个必要条件(面问)

(1) 互斥条件:一个资源每次只能被一个进程或线程使用。(若死锁可先把阻塞锁换

                                成非阻塞锁找出错误后改回)

(2) 请求与保持条件:一个进程或线程因请求资源而阻塞时,对已获得的资源保持不

                                        放。

(3) 不剥夺条件:进或线程程已获得的资源,在末使用完之前,不能强行剥夺。

(4) 循环等待条件:若干进程或线程之间形成一种头尾相接的循环等待资源关系。

相关文章:

  • 界面控件DevExpress WinForms中文教程:Banded Grid View - 如何固定Bands?
  • ESP32对接巴法云实现配网
  • IntelliJ IDEA 中进行背景设置
  • Python使用
  • 【工作笔记】 WSL开启报错
  • 参数化建模(三):SOLIDWORKS中的参数化应用实例
  • docker部署自动化测试环境笔记
  • (21)量子计算对密码学的影响
  • Redis持久化机制
  • 力扣HOT100之动态规划:322. 零钱兑换
  • 【大模型】情绪对话模型项目研发
  • 区域未停留检测算法AI智能分析网关V4打造铁道/工厂/机场等场景应用方案
  • 2025 年 Solana 生态全景分析:它如何从以太坊「高速替代方案」成长为成熟的基础设施?
  • 换ip是换网络的意思吗?怎么换ip地址
  • write和read命令中的通道号指南
  • 使用Vditor将Markdown文档渲染成网页(Vite+JS+Vditor)
  • LangChain第二页_【教程】翻译完了
  • 将Kotti从Pyramid1.0升级到2.0 (失败的记录)
  • 【Linux 基础知识系列】第三篇-Linux 基本命令
  • JavaScript 变量声明:var vs let vs const详情
  • 云南网站seo外包/seo大牛
  • 建网站自己做服务器/软文案例大全
  • 个体户可以做企业网站/如何进行品牌营销
  • 福田网站制作公司/企业文化标语
  • 做电商平台网站/网络营销是干什么的
  • 做网站你给推广/百度店铺注册