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

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 ;
}

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

相关文章:

  • 开关电源实战(六)ADDC反激电源
  • 【MySQL数据库】函数操作
  • PH热榜 | 2025-04-27
  • 论文速报《ChatBEV:理解BEV地图的视觉语言模型新突破》
  • H5实现一个二维码生成器页面
  • 【MySQL】Java代码操作MySQL数据库 —— JDBC编程
  • 接口测试详解
  • 【Luogu】动态规划六
  • vue3子传父——v-model辅助值传递
  • C++ ——引用
  • 详细PostMan的安装和基本使用方法
  • 低压电工证考试的实操部分主要考察哪些内容?
  • 邀请函|2025 Altair区域技术交流会华北站,报名开启!
  • 安卓基础(适配器和RecyclerView )
  • 【HPC存储性能测试】02-ior带宽性能测试
  • Bolt.diy 一键部署,“一句话”实现全栈开发
  • GPUStack昇腾Atlas300I duo部署模型DeepSeek-R1【GPUStack实战篇2】
  • Java安全之cc链学习集合
  • 【MySQL 】MySQL 安装自记录全程-详细 (mysql-installer-community-8.0.42.0.msi)
  • XLSX.utils.sheet_to_json设置了blankrows:true,但无法获取到开头的空白行
  • 加拿大今日大选:房价、印度移民和特朗普,年轻人在焦虑什么?
  • 加拿大驾车撞人事件遇难人数升到11人
  • 中日友好医院通报“医师肖某被举报”:基本属实,开除党籍并解聘
  • 图像编辑新增一款开源模型,阶跃星辰发布Step1X-Edit
  • 清华成立人工智能医院,将构建“AI+医疗+教育+科研”闭环
  • 蚂蚁财富28亿港元要约收购耀才证券,筹谋香港券商牌照