Linux系统编程 day11 锁 (两天没有更新了,中期完就休息了)

锁的注意事项

1、尽量保证锁的粒度,越小越好。(访问共享数据前,加锁,访问结束后立即解锁)

2、互斥锁,本质是结构体,但是可以看成整数,初值为1。(pthread_mutex_init调用成功)

3、加锁: --操作,阻塞线程

4、解锁:++操作,唤醒阻塞在锁上的进程

try锁:尝试加锁 , 成功-- , 失败:返回错误号(EBUSY),不阻塞。

读写锁(三句话)

读写锁只有一把,但是具备两种状态。

“写模式加锁”:解锁前,所有对该锁加锁的线程都会被阻塞。

“读模式加锁”:其他线程用读模式加锁会成功,写模式加锁则阻塞。

“读模式加锁”:既有试图写模式加锁的线程也有读模式加锁的线程,那么读写锁会阻塞随后的读模式锁请求,优先满足写模式锁,读写锁并行阻塞写锁优先级高。

写独占、读共享

相较于互斥量而言,当读线程多的时候,提高访问效率。锁一般都定义成全局变量。

主要应用函数pthread_rwlock_xxx

pthread_rwlock_t rwlock;   创建读写锁pthread_rwlock_init
pthread_rwlock_destroy
pthread_rwlock_rdlock
pthread_rwlock_wrlock
pthread_rwlock_tryrdlock
pthread_rwlock_trywrlock
pthread_rwlock_unlock成功返回0,失败返回错误号

 3个线程不定时写同一全局资源 , 5个线程不定时读同一全局资源.

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>pthread_rwlock_t rwlock;
int counter = 20;void* th_write(void* arg){int t;int ret;int i = (int)arg;while(1){ret = pthread_rwlock_wrlock(&rwlock);if(ret != 0){fprintf(stderr , "pthread_rwlock_wrlock error with :%s\n" , strerror(ret));exit(1);}t = counter;usleep(1000);printf("-----write %d: %lu: counter=%d , counter++ = %d\n" , i , pthread_self() , t , ++counter);ret = pthread_rwlock_unlock(&rwlock);if(ret != 0){fprintf(stderr , "pthread_rwlock_unlock error with :%s\n" , strerror(ret));exit(1);}usleep(10000);}return NULL;
}void* th_read(void* arg){int i = (int)arg;int ret;while(1){ret = pthread_rwlock_rdlock(&rwlock);if(ret != 0){fprintf(stderr , "pthread_rwlock_rdlock error with :%s\n" , strerror(ret));exit(1);}printf("-------read %d: %lu: counter = %d\n", i , pthread_self() , counter);ret = pthread_rwlock_unlock(&rwlock);if(ret != 0){fprintf(stderr , "pthread_rwlock_unlock error with :%s\n" , strerror(ret));exit(1);}usleep(2000);}return NULL;
}int main(int argc , char *argv[])
{pthread_t tid[8];int ret ;int i ;ret = pthread_rwlock_init(&rwlock , NULL);if(ret != 0){fprintf(stderr , "pthread_rwlock_init error with :%s\n" , strerror(ret));exit(1);}for(i =0 ; i < 3 ; i++ ){ret = pthread_create(&tid[i], NULL , th_write ,(void*)i);if(ret != 0){fprintf(stderr , "pthread_create error with :%s\n" , strerror(ret));exit(1);}}for(i = 0 ; i < 5 ; i++){ret = pthread_create(&tid[i+3] , NULL , th_read , (void*)i);if(ret != 0){fprintf(stderr , "pthread_create error with :%s\n" , strerror(ret));exit(1);}}for(i = 0 ; i < 8 ; i++){pthread_join(&tid[i] , NULL);if(ret != 0){fprintf(stderr , "pthread_join error with :%s\n" , strerror(ret));exit(1);}}pthread_rwlock_destroy(&rwlock);return 0 ;
}

死锁

使用锁不恰当导致的现象。条件

1、线程试图对同一个互斥量加锁两次

2、线程1拥有A锁 , 请求获得B锁 , 线程2拥有B锁,请求获得A锁。

条件变量

本身不是锁,但是通常要结合锁来使用。(等待某一个变量满足)。

pthread_cond_t cond;   创建读写锁pthread_cond_init
pthread_cond_destroy
pthread_cond_wait
pthread_cond_timewait
pthread_cond_signal
pthread_cond_broadcast成功返回0,失败返回错误号

pthread_cond_wait函数

阻塞等待一个条件变量

int pthread_cond_wait(&cond , pthread_mutex_t *restrict murex);

函数作用:
1、阻塞等待条件变量cond满足。

2、释放已掌握的互斥锁(解锁),相当于pthread_mutex_unlock(&mutex);   1,2两步为一个原子操作

3、当被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁Pthread_mutex_lock(&mutex).

 生产者、消费者模型

pthread_cond_signal函数

唤醒阻塞在条件变量上的一个线程

pthread_cond_broadcast函数

唤醒阻塞在条件变量上的多个线程

要求借助条件变量完成生产者消费者模型

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>struct msg{int num;struct msg* next;
};pthread_mutex_t mutex;
pthread_cond_t has_data = PTHREAD_COND_INITIALIZER;struct msg * head;void err_thread(int ret , char*str)
{if(ret != 0){fprintf(stderr , "%s:%s\n" ,str ,  strerror(ret));pthread_exit(NULL);}
}void* produser(void* arg)
{int ret ;while(1){struct msg *mp;mp = malloc(sizeof(struct msg));mp->num = rand() % 1000 + 1;pthread_mutex_lock(&mutex);if(ret != 0)err_thread(ret , "pthread_mutex_lock error");head = mp;printf("------prod:%d , pid:%lu\n" , mp->num , pthread_self());pthread_mutex_unlock(&mutex);if(ret != 0)err_thread(ret , "pthread_mutex_unlock error");ret = pthread_cond_signal(&has_data);if(ret != 0)err_thread(ret , "pthread_mutex_lock error");sleep(rand() % 3);}return NULL;
}void* consumer(void* arg)
{int ret;while(1){struct msg *mp;mp = head;ret = pthread_mutex_lock(&mutex);if(ret != 0)err_thread(ret , "pthread_mutex_lock error");while(head == NULL){ret = pthread_cond_wait(&has_data , &mutex);  // pthread_cond_wait函数返回时,重新加锁mutex。if(ret != 0 )err_thread(ret , "pthread_con_wait error");}mp = head;head = mp->next;ret = pthread_mutex_unlock(&mutex);if(ret != 0)err_thread(ret , "pthread_mutex_lock error");printf("-------cons:%d , cid:%lu\n" , mp->num , pthread_self());sleep(rand()%3);free(mp);}return NULL;
}int main(int argc , char *argv[])
{pthread_t pid , cid;srand(time(NULL));int ret;ret = pthread_mutex_init(&mutex , NULL);if(ret != 0)err_thread(ret , "pthread_create error");ret = pthread_create(&pid , NULL , produser , NULL);if(ret != 0)err_thread(ret , "pthread_create error");ret = pthread_create(&cid , NULL , consumer , NULL);if(ret != 0)err_thread(ret , "pthread_create error");pthread_join(pid , NULL);pthread_join(cid , NULL);return 0 ;
}

信号量

应用于线程、进程间同步。

相当于初始化值为 N 的互斥量。 N 为可同时访问的进程数。

sem_init()
sem_destroy() 
sem_wait()   //相当于加锁
sem_trywait()
sem_timewait()
sem_post()   //相当于解锁返回值:成功返回0 , 失败返回errno

sem_wait 信号量>0,信号量-- ;信号量 = 0 ,线程阻塞。  对比pthread_mutex_lock()。 

sem_post 信号量++, 唤醒阻塞在信号量上的线程

信号量的初值,决定了占用信号量的线程个数。

 sem_init()函数

int sem_init(sem_t *sem , int pshared , unsigned int value);pshared:  0:用于线程间同步1:用于进程间同步value :N值 (指定同时访问的数目)

信号量的生产者消费者模型

 

主要是明白wait和post的关系,以及阻塞在哪的关系。 

重点:

sem_wait 信号量>0,信号量-- ;信号量 = 0 ,线程阻塞。  对比pthread_mutex_lock()。 

sem_post 信号量++, 唤醒阻塞在信号量上的线程

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<semaphore.h>
#define NUM 5
int queue[NUM];
sem_t blank_number;
sem_t stat_number;void err_thread(int ret , char*str)
{if(ret != 0 ){fprintf(stderr , "%s:%s\n" , str , strerror(ret));pthread_exit(NULL);}
}void* produser(void* arg)
{int i = 0;while(1){sem_wait(&blank_number);//空格子数--queue[i] = rand()%1000 + 1;printf("---produce:%d\n" , queue[i]);sem_post(&stat_number); // 产品数++i = (i+1) % NUM; // 借助队列下标实现环形队列sleep(rand()%1);}return NULL;
}void* consumer(void* arg)
{int i = 0;while(1){sem_wait(&stat_number);printf("-----consume:%d\n" , queue[i]);queue[i] = 0;sem_post(&blank_number);i = (i + 1)% NUM;sleep(rand()%3);}return NULL;
}int main(int argc , char *argv[])
{pthread_t pid , cid;int ret ;srand(time(NULL));sem_init(&blank_number , 0 , NUM);sem_init(&stat_number , 0 , 0);ret = pthread_create(&pid , NULL , produser , NULL);if(ret != 0)err_thread(ret , "pthread_create error");ret = pthread_create(&cid , NULL , consumer , NULL);if(ret != 0)err_thread(ret , "pthread_create error");pthread_join(pid , NULL);pthread_join(cid , NULL);sem_destroy(&blank_number);sem_destroy(&stat_number);return 0 ;
}

系统编程完结撒花!开始网络编程咯~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/78446.shtml

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

相关文章

【Maven】特殊pom.xml配置文件 - BOM

文章目录 特殊pom.xml配置文件 - BOM一、例子二、注意事项1.特殊的子pom.xml文件2.dependencyManagement 特殊pom.xml配置文件 - BOM 仅用于集中管理项目依赖版本 在 Maven 中&#xff0c;BOM 用于定义一个项目的依赖版本的集合&#xff0c;通常用于管理一组共享的依赖版本。这…

《代码整洁之道》第5章 格式 - 笔记

你应该选择一套管理代码格式的简单规则。如果是团队&#xff0c;应该选择一套团队一致同意采用的简单格式规则。 最重要的原则&#xff1a;一致性&#xff08;Consistency&#xff09;&#xff01; 没有完美的格式规范&#xff0c;但有统一的规范。 整个团队&#xff08;或者…

C++ 类与对象(中)—— 默认成员函数与运算符重载的深度解析:构造函数,析构函数,拷贝构造函数,赋值运算符重载,普通取地址重载,const取地址重载

在 C 中&#xff0c;类的默认成员函数是编译器自动生成的重要机制&#xff0c;合理利用这些函数可以简化代码编写&#xff0c;同时避免资源管理错误。本文将从构造函数、析构函数、拷贝构造函数、赋值运算符重载等核心内容展开&#xff0c;结合具体案例深入解析。 一、默认成员…

【KWDB创作者计划】_企业级多模数据库实战:用KWDB实现时序+关系数据毫秒级融合(附代码、性能优化与架构图)

一、技术背景与行业痛点 1.1 多模数据融合挑战 场景痛点&#xff1a; 工业物联网设备每秒产生百万级传感器数据&#xff08;时序数据&#xff09;。需关联设备档案&#xff08;关系数据&#xff09;生成设备健康报告&#xff0c;传统方案需多数据库跳转&#xff0c;延迟>5…

w~嵌入式C语言~合集4

我自己的原文哦~ https://blog.51cto.com/whaosoft/13870376 一、STM32怎么选型 什么是 STM32 STM32&#xff0c;从字面上来理解&#xff0c;ST是意法半导体&#xff0c;M是Microelectronics的缩写&#xff0c;32表示32位&#xff0c;合起来理解&#xff0c;STM32就是指S…

Multisim使用教程详尽版--(2025最新版)

一、Multisim14前言 1.1、主流电路仿真软件 1. Multisim&#xff1a;NI开发的SPICE标准仿真工具&#xff0c;支持模拟/数字电路混合仿真&#xff0c;内置丰富的元件库和虚拟仪器&#xff08;示波器、频谱仪等&#xff09;&#xff0c;适合教学和竞赛设计。官网&#xff1a;艾…

分布式理论和事务

微服务和分布式 微服务 是一种软件架构风格&#xff0c;它将应用程序拆分成一系列小型、独立的服务&#xff0c;每个服务专注于单一功能&#xff0c;彼此通过轻量级通信机制&#xff08;如 API&#xff09;进行交互。微服务通常是松耦合的&#xff0c;可以独立开发、部署和扩展…

JAVA:红黑树应用的技术指南

&#x1f333; 1、简述 红黑树是一种自平衡二叉查找树&#xff08;Self-Balancing Binary Search Tree&#xff09;&#xff0c;被广泛应用于操作系统调度、Java集合、数据库索引等核心模块中。本文将从 基本原理 入手&#xff0c;结合 实际应用场景与代码实例&#xff0c;带你…

【Pandas】pandas DataFrame rfloordiv

Pandas2.2 DataFrame Binary operator functions 方法描述DataFrame.add(other)用于执行 DataFrame 与另一个对象&#xff08;如 DataFrame、Series 或标量&#xff09;的逐元素加法操作DataFrame.add(other[, axis, level, fill_value])用于执行 DataFrame 与另一个对象&…

【数据可视化-26】基于人口统计与社会经济数据的多维度可视化分析

🧑 博主简介:曾任某智慧城市类企业算法总监,目前在美国市场的物流公司从事高级算法工程师一职,深耕人工智能领域,精通python数据挖掘、可视化、机器学习等,发表过AI相关的专利并多次在AI类比赛中获奖。CSDN人工智能领域的优质创作者,提供AI相关的技术咨询、项目开发和个…

WinForm真入门(18)——DateTimePicker‌控件解析

一、基本概念‌ ‌DateTimePicker‌ 是 Windows 窗体中用于选择日期和时间的控件&#xff0c;支持以下交互方式&#xff1a; 通过下拉日历选择日期通过上下按钮调整时间直接输入日期或时间 适用于需要规范日期格式、限制日期范围或快速输入的场景&#xff08;如预约系统、数据…

AVFormatContext 再分析

说明 &#xff1a;将 avfromatContext 的变量依次打印分析&#xff0c;根据ffmpeg 给的说明&#xff0c;猜测&#xff0c;结合网上的文章字节写测试代码分析。 从常用到不常用依次分析 1. unsigned int nb_streams; 代表 avfromatContext 中 AVStream **streams 的个数 /** …

计算机网络-运输层(1)

计算机网络-运输层(1) 文章目录 计算机网络-运输层(1)5.1 运输层概述5.2 运输层端口号、复用与分用端口号基本概念端口号特性端口号分类重要说明 5.3 UDP与TCP协议对比关键区别说明 5.1 运输层概述 计算机网络体系结构中的物理层、数据链路层以及网络层共同解决了主机通过异构…

2025 FIC wp

这次比赛计算机和手机大部分题目都比较常规 第一和第四部分有点让人摸不着头脑 比赛的时候第一部分有四个题没出 第四部分基本都没怎么出 现在复盘一下 把我当时做题的心得和获取的新知识记录一下 互联网取证的部分就先学习一下别的师傅 检材 链接&#xff1a;https://pan.bai…

【大数据技术-联邦集群RBF】DFSRouter日志一直打印修改Membership为EXPIRED状态的日志分析

生产环境遇到下面报错 2025-04-23 17:44:15,780 INFO store.CachedRecordStore (CachedRecordStore.java:overrideExpiredRecords(192)) - Override State Store record MembershipState: router1:8888->hh-fed-sub25:nn2:nn2:8020-EXPIRED 2025-04-23 17:44:15,781 INFO …

【HarmonyOS 5】鸿蒙检测系统完整性

【HarmonyOS 5】鸿蒙检测系统完整性 一、前言 从现实安全威胁来看&#xff0c;设备系统完整性风险已影响至移动应用的各个场景。不少用户因使用越狱设备&#xff08;Jailbreak&#xff09;或非真实设备&#xff08;Emulator&#xff09;&#xff0c;导致应用安全防护机制失效…

学习spark-streaming收获

1.流处理的核心概念 •实时 vs微批处理&#xff1a;理解了 Spark Streaming 的微批处理&#xff08;Micro-Batch&#xff09;模型&#xff0c;将流数据切分为小批次&#xff08;如1秒间隔&#xff09;进行处理&#xff0c;与真正的流处理&#xff08;如Flink&#xff09;的区…

Redis一些小记录

Redis一些小记录 SpringData Redis&#xff1a;RedisTemplate配置与数据操作 操作String类型数据 String是Redis中最基本的数据类型&#xff0c;可以存储字符串、整数或浮点数。RedisTemplate提供了ValueOperations接口来操作String类型的数据&#xff0c;支持设置值、获取值、…

5G融合消息PaaS项目深度解析 - Java架构师面试实战

5G融合消息PaaS项目深度解析 - Java架构师面试实战 场景&#xff1a;互联网大厂Java求职者面试&#xff0c;面试官针对5G融合消息PaaS项目进行提问。 第一轮提问 面试官&#xff1a;马架构&#xff0c;请简要介绍5G融合消息PaaS平台的核心功能和应用场景。 马架构&#xff…

【C语言极简自学笔记】C 语言数组详解:一维数组与二维数组

在 C 语言中&#xff0c;数组是一种非常重要的数据结构&#xff0c;它可以将多个相同类型的元素组织在一起&#xff0c;以便于我们进行批量处理和操作。本文将详细介绍 C 语言中的一维数组和二维数组&#xff0c;包括它们的定义、初始化、元素访问以及内存存储等方面的内容。 …