06.线程同步

互斥锁(互斥量)

描述

一个进程下的线程是共享资源的,通信便利的同时也造成了许多麻烦,线程程和线程之间如果同时访问一块资源就会出错,所以要引入一个互斥变量给它加锁,让它去协同不同线程程之间的访问(线程同步)

pthread_mutex_init函数

函数描述

初始化一个互斥量

函数原型

int pthread_mutex_init(pthread_mutex_t *mtx,const pthread_mutexattr *attr)
参一:传出参数,互斥量
参二:互斥量属性的指针

pthread_mutex_lock函数

阻塞加锁,如果没有加锁就加锁,有锁的话阻塞等待锁的消失(对于互斥量而言)
int pthread_mutex_lock(pthread_mutex_t *mtx)

pthread_mutex_unlock函数

int pthread_mutex_unlock(pthread_mutex_t *mtx)

pthread_mutex_destroy函数

int pthread_mutex_destroy(pthread_mutex_t *mtx)
#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<stdlib.h>
const int N=1e6;
int a;
pthread_mutex_t mtx;//定义互斥变量
void* thread1(void* arg){
    for(int i = 1;i <= N; i++){
        //首先会获取mtx,判断mtx是否加锁,如果加锁了,会阻塞等待
        //知道持有互斥锁的线程解锁,然后当前进程加锁
        //如果没有加锁,直接给mtx上锁继续执行下面代码
        pthread_mutex_lock(&mtx);
        a++;
        //给互斥变量解锁
        pthread_mutex_unlock(&mtx);
        }
}
int main(int argc, char* argv[])
{
    pthread_t tids[10];
    pthread_mutex_init(&mtx,NULL);//初始化互斥变量

    for(int i = 0;i < 10; i++){
        pthread_create(tids+i,NULL,thread1,NULL);
    }
    for(int i = 0;i < 10; i++){
        pthread_join(tids[i],NULL);
    }
    printf("a = %d\n",a);
    pthread_mutex_destroy(&mtx);
    return 0;
}

pthread_mutex_trylock函数

非阻塞加锁(可以解决死锁问题)

练习

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<stdlib.h>
#include<time.h>
int seat[11],seat_num=10,thread_num=12;
pthread_t thread[13];
pthread_mutex_t mtx;
int get_id(){
    //利用随机函数生成随机数,每次生成的随机数与末尾的调换
    int r_id,res;
    if(seat_num==0) return 0;
    r_id=rand()%seat_num+1;
    res=seat[r_id];
    seat[r_id]=seat[seat_num];
    seat_num--;
    return res;
}
void* thread1(void* arg){
    pthread_mutex_lock(&mtx);
    int seat_id=get_id();
    pthread_mutex_unlock(&mtx);
    if(seat_id==0){
        printf("用户 %d 选座失败,座位已售空\n",*(int*)arg);
    }else{
        printf("用户 %d 成功选得座位 %d\n",*(int*)arg,seat_id);
    }
    return NULL;
}
int main(int argc, char* argv[])
{
    srand((unsigned)time(0));
    for(int i=1;i<=10;i++){
        seat[i]=i;
    }
    pthread_mutex_init(&mtx,NULL);
    for(int i=1;i<=12;i++){
        int* user_id=(int*)malloc(sizeof(int));
        *user_id=i;
        pthread_create(&thread[i],NULL,thread1,user_id);
    }
    for(int i=1;i<=12;i++){
        pthread_join(thread[i],NULL);
    }
    pthread_mutex_destroy(&mtx);
    return 0;
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

死锁现象

描述

当多个线程为了保护多个共享资源使用了多个互斥锁,如果使用不恰当,就可能造成多个线程之间一直阻塞等待对方锁的释放,这种现象叫死锁
具体例子

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在又俩个线程,A线程和B线程,现在同时运行,A线程给互斥量1上了锁,B线程给互斥量2上了锁,然后sleep了1s,此时A线程持有互斥量1并等待B线程互斥量2解锁,B线程中持有互斥量2并等待A线程互斥量1解锁,AB线程互相等待,产生死锁
#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<stdlib.h>
#include<time.h>
pthread_mutex_t mtx1,mtx2;
void* thread1(void* arg){
    printf("child get mtx1.........\n");
    pthread_mutex_lock(&mtx1);
    printf("child get mtx1 success!\n");
    sleep(1);
    printf("child get mtx2.........\n");
    pthread_mutex_lock(&mtx2);
    printf("child get mtx2 success!\n");
    pthread_mutex_unlock(&mtx1);
    pthread_mutex_unlock(&mtx2);
}
int main(int argc, char* argv[])
{
    pthread_mutex_init(&mtx1,NULL);
    pthread_mutex_init(&mtx2,NULL);
    pthread_t tid;
    pthread_create(&tid,NULL,thread1,NULL);
    printf("main get mtx2.........\n");
    pthread_mutex_lock(&mtx2);
    printf("main get mtx2 success!\n");
    sleep(1);
    printf("main get mtx1.........\n");
    pthread_mutex_lock(&mtx1);
    printf("main get mtx1 success!\n");
    pthread_mutex_unlock(&mtx2);
    pthread_mutex_unlock(&mtx1);
    return 0;
}

死锁产生的四个必要条件

1.互斥条件:必须得有多个互斥变量

2.持有并等待条件:A持有锁1并等待B锁2解锁,B持有锁2并等待A锁1解锁

3.不可剥夺条件:必须一直阻塞

4.环路等待条件

避免死锁

1.不加锁(不太现实奥)

2.锁的力度控制:尽量减少持有锁的事件,降低死锁发生概率

3.资源有序分配:破坏环路条件1 2 1 2

4.重试机制:破坏不可剥夺条件,一般为非阻塞的trylock

#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<stdlib.h>
#include<time.h>
#include <errno.h>
pthread_mutex_t mtx1,mtx2;
void* thread1(void* arg){
    while(1){
        printf("child get mtx1.........\n");
        pthread_mutex_lock(&mtx1);
        printf("child get mtx1 success!\n");
        sleep(1);
        printf("child get mtx2.........\n");
        int ret = pthread_mutex_trylock(&mtx2);
        if(ret != 0 && ret == EBUSY){
            pthread_mutex_unlock(&mtx1);
            printf("child free mtx1\n");
            continue;
        }
        printf("child get mtx2 success!\n");
        pthread_mutex_unlock(&mtx1);
        pthread_mutex_unlock(&mtx2);
        return NULL;
    }
}
int main(int argc, char* argv[])
{
    pthread_mutex_init(&mtx1,NULL);
    pthread_mutex_init(&mtx2,NULL);
    pthread_t tid;
    pthread_create(&tid,NULL,thread1,NULL);
    printf("main get mtx2.........\n");
    pthread_mutex_lock(&mtx2);
    printf("main get mtx2 success!\n");
    sleep(1);
    printf("main get mtx1.........\n");
    pthread_mutex_lock(&mtx1);
    printf("main get mtx1 success!\n");
    pthread_mutex_unlock(&mtx2);
    pthread_mutex_unlock(&mtx1);
    pthread_exit(NULL);
    return 0;
}

读写锁

已经有互斥锁了为什么还要有读写锁?

当多个线程读多写少的时候建议使用读写锁

概述

读写锁由读锁和写锁两部分构成,特性为写独占,读共享。
适用于写多读少的情况

特性

写独占,读共享

一个线程写的时候,另一个线程不能写
一个线程读的时候,另一个线程可以读

读写锁函数

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

场景分析

持有读锁时,申请读锁,加锁成功,不需要等待
持有写锁时,申请写锁,会阻塞等待写锁的解锁,然后再加锁
持有读锁时,申请写锁,会阻塞等待读锁的解锁,然后再加锁
持有写锁时,申请读锁,会阻塞等带写锁的写锁,然后再加锁
持有读锁时,申请写锁和读锁,读锁会加锁成功,写锁会阻塞等待(如果是很多的读锁和少量的写锁的话,写锁会一直无法请求成功,造成饥饿)
持有写锁时,申请写锁和读锁,(如果是读优先)读锁会加锁成功,写锁阻塞等待写锁解锁,然后再加锁
#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<stdlib.h>
#include<time.h>
#include <errno.h>
pthread_rwlock_t rwlock;
void* thread1(void* arg){
    pthread_rwlock_wrlock(&rwlock);
    printf(" thread1 加锁成功\n");
    sleep(2);
    pthread_rwlock_unlock(&rwlock);
    printf("thread1 解锁\n");

}
void* thread2(void* arg){
    pthread_rwlock_wrlock(&rwlock);
    printf(" thread2 加锁成功\n");
    sleep(2);
    pthread_rwlock_unlock(&rwlock);
    printf("thread2 解锁\n");
}
void* thread3(void* arg){
    pthread_rwlock_rdlock(&rwlock);
    printf(" thread3 加锁成功\n");
    sleep(2);
    pthread_rwlock_unlock(&rwlock);
    printf("thread3 解锁\n");
}
int main(int argc, char* argv[])
{
    pthread_rwlock_init(&rwlock,NULL);
    pthread_t tid;
    pthread_create(&tid,NULL,thread1,NULL);
    sleep(1);
    pthread_create(&tid,NULL,thread2,NULL);
    sleep(1);
    pthread_create(&tid,NULL,thread3,NULL);
    pthread_exit(NULL);
    return 0;
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

练习

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<stdlib.h>
#include<time.h>
#include <errno.h>
pthread_rwlock_t rwlock;
int balance;

void* save(void* arg){
    int val = atoi((char*)arg);
    pthread_rwlock_wrlock(&rwlock);
    sleep(1);
    balance+=val;
    printf("save money:%d , balance:%d\n",val,balance);
    pthread_rwlock_unlock(&rwlock);

}
void* query(void* arg){
    pthread_rwlock_rdlock(&rwlock);
    int r = rand()%3+1;
    sleep(r);
    printf("query : balance : %d\n",balance);
    pthread_rwlock_unlock(&rwlock);
}

int main(int argc, char* argv[])
{
    srand((unsigned)time(0));
    pthread_t threads[15];
    pthread_rwlock_init(&rwlock,NULL);
    for(int i=0;i<5;i++){
        pthread_create(&threads[i],NULL,save,argv[i+1]);
    }
    for(int i=5;i<15;i++){
        pthread_create(&threads[i],NULL,query,NULL);
    }
    for(int i=0;i<15;i++){
        pthread_join(threads[i],NULL);
    }
    pthread_rwlock_destroy(&rwlock);
    return 0;
}

条件变量

条件变量用于进程同步当中。条件变量允许一个线程阻塞等待某个条件的成立,改线程会被挂起,不占用cpu资源。当条件满足的时候,可以唤醒阻塞等待的线程继续执行
条件变量一般与互斥锁一同使用

函数

int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr);
//条件变量的初始化
int pthread_cond_destroy(pthread_cond_t *cond)
//条件变量的销毁
int pthread_cond_wait(pthread_cond_t *t)
//阻塞等待某个条件变量,释放持有的互斥锁,这俩步是原子操作
//直到被唤醒,接触阻塞重新获得互斥锁
#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
int a;
pthread_mutex_t mtx;
pthread_cond_t cond;
void* thread1(void* arg){
    for(int i=0;i<10;i++){
        //在哪访问共享资源就在哪加锁
        pthread_mutex_lock(&mtx);
        while (a != 0)
        {
            //避免轮询操作
            pthread_cond_wait(&cond,&mtx);
            //释放锁并阻塞等待,这两步是原子操作不可分割 
        }
        printf("a = %d\n", ++a);
        pthread_cond_broadcast(&cond);
        pthread_mutex_unlock(&mtx);

    }  
}
void* thread2(void* arg){
    for(int i=0;i<10;i++){
        pthread_mutex_lock(&mtx);
        while (a != 1)
        {
            pthread_cond_wait(&cond,&mtx);
        }
        printf("a = %d\n", --a);
        pthread_cond_broadcast(&cond);
        pthread_mutex_unlock(&mtx);
    }
}
int main(int argc, char* argv[])
{
    pthread_mutex_init(&mtx,NULL);
    pthread_t tid1,tid2,tid3,tid4;
    pthread_create(&tid1,NULL,thread1,NULL);
    pthread_create(&tid2,NULL,thread2,NULL);
    pthread_create(&tid3,NULL,thread1,NULL);
    pthread_create(&tid4,NULL,thread2,NULL);
    
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);
    pthread_join(tid4,NULL);
    return 0;
}

生产者消费者问题

#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<time.h>
#include<stdlib.h>

pthread_mutex_t mtx;
pthread_cond_t cond;

struct node* head;

struct node{
    int val;//数据域
    struct node* next;//指针域
};

void add_val(int val){
    struct node* p=(struct node*)malloc(sizeof(struct node));
    p->val=val;
    p->next=head;
    head=p;
}

int get_val(){
    if(!head){
        return -1;
    }
    int ans=head->val;
    struct node* tmp=head;
    head = head->next;
    free(tmp);
    return ans;
}

void* producer(void* arg){
    while(1){
        pthread_mutex_lock(&mtx);
        add_val(rand()%500+1);
        //说明肯定有数据了
        pthread_cond_broadcast(&cond);
        pthread_mutex_unlock(&mtx);
    }
}
void* conmuser(void* arg){
    while(1){
        pthread_mutex_lock(&mtx);
        //如果链表中没有数据了,那么就需要阻塞等待数据的产生
        int num=get_val();
        if(num == -1){
            pthread_cond_wait(&cond,&mtx);
        }
        printf("num = %d\n",num);
        sleep(1);
        pthread_mutex_unlock(&mtx);

    }
}
int main(int argc, char* argv[])
{
    srand(time(0));
    pthread_mutex_init(&mtx,NULL);
    pthread_cond_init(&cond,NULL);
    pthread_t tid1,tid2,tid3,tid4;
    pthread_create(&tid1,NULL,producer,NULL);
    pthread_create(&tid2,NULL,conmuser,NULL);
    
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    return 0;
}

信号量

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

两个线程堆a自增1万次

#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<semaphore.h>

int a;
sem_t sem;
void* thread1(void* arg){
    for(int i=0;i<100000;i++){
        sem_wait(&sem);
        a++;
        sem_post(&sem);
    }
}
int main(int argc, char* argv[])
{
    sem_init(&sem,0,1);
    pthread_t tid1,tid2;
    pthread_create(&tid1,NULL,thread1,NULL);    
    pthread_create(&tid2,NULL,thread1,NULL);
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    printf("a = %d\n",a);    
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++对象的赋值

同类的对象之间可以互相赋值&#xff0c;即一个对象的值可以赋值给另一个对象。对象之间的赋值通过“”进行。默认就是把一个对象所有非static数据成员的值依次赋值给另一个对象。 对象赋值的一般形式为&#xff1a; 对象名1 对象名2; 注意:对象名1和对象名2必须是属于同一个…

4000字超详解Linux权限

各位大佬好 &#xff0c;这里是阿川的博客 &#xff0c; 祝您变得更强 个人主页&#xff1a;在线OJ的阿川 大佬的支持和鼓励&#xff0c;将是我成长路上最大的动力 阿川水平有限&#xff0c;如有错误&#xff0c;欢迎大佬指正 在Linux当中权限的体现主要有两种 普通用户 超…

重装前端整体流程

用户管理 --汇总 -- 明细-CSDN博客 一、node 这个看环境变量 2023最新版Node.js下载安装及环境配置教程&#xff08;非常详细&#xff09;从零基础入门到精通&#xff0c;看完这一篇就够了_nodejs安装及环境配置-CSDN博客 配置到国内镜像的时候&#xff0c;去看&#xff0c;淘…

linux上安装Jmeter环境

以前都是在Windows本机上使用界面版Jmeter&#xff0c;今天试一下安装到linux上在linux中使用&#xff0c;Jmeter的使用需要先安装jdk环境然后再配置jmeter。 1.配置环境 linux环境&#xff1a;Centos 8.2 64位 JDK版本&#xff1a;jdk-8u221-linux-x64.tar.gz &#xff08;…

ICode国际青少年编程竞赛- Python-4级训练场-绿色飞板1

ICode国际青少年编程竞赛- Python-4级训练场-绿色飞板1 1、 while Flyer.disappear():wait() Dev.step(4)2、 Dev.turnRight() Dev.step()while Flyer[0].disappear():wait() Dev.step(3) Dev.turnLeft() Dev.step() while Flyer[1].disappear():wait() Dev.step(2) Dev.tu…

牛信云:以客户为中心打造全流程营销闭环 | 企业出海案例精选

自2018年成立以来&#xff0c;深圳牛信网络科技有限公司&#xff08;以下简称“牛信云”&#xff09;一直坚持以技术驱动为核心&#xff0c;并注重全球资源整合以及服务体系的搭建&#xff0c;目前&#xff0c;已经形成了以新加坡为中心&#xff0c;以中国、印尼、马来西亚、荷…

uni-app(四):原生插件开发(Android)

原生插件开发 原生插件开发module1.创建模块2.解决报错3.修改依赖4.编写插件代码5.添加插件配置6.引入模块7.调用插件代码8.运行 component1.创建模块2.解决报错3.修改依赖4.编写插件代码5.添加插件配置6.引入模块7.调用插件代码8.运行 原生插件开发 主要分为两类扩展: Module:…

Ubuntu系统下编译OpenCV4.8源码

OpenCV4.8源码编译与安装 其实很简单&#xff0c;只要三步即可搞定&#xff0c;第一步是下载指定版本的源码包&#xff1b;第二步是安装OpenCV4.8编译需要的编译器与第三方库支持&#xff1b;第三步就是编译OpenCV源码包生成安装文件并安装。 01下载OpenCV4.8源码包 在Ubunt…

JavaScript事件对象

华子目录 事件对象js事件驱动机制 三种事件模型1.标签绑定2.DOM0事件模型3.DOM2事件模型捕获流与冒泡流removeEventListener事件移除 事件类型鼠标事件mouse event键盘事件keyboard event多媒体事件media event表单事件form event窗口事件window event其他事件 事件对象切换图片…

适用于 macOS 的最佳独立 HBO Max 客户端

适用于 macOS 的最佳独立 HBO Max 应用程序。不再在浏览器选项卡之间切换。只需直接从 Dock 启动 Clicker for HBO Max 即可开始狂欢。 HBO Max 客户端 Clicker for HBO Max 下载 Clicker for HBO Max mac版安装教程 软件下载完成后&#xff0c;双击pkg根据提示进行安装 Clic…

LineVul实验复现及相关问题

最近在复现 LineVul 这篇文章的实验&#xff0c;本文主要用于简化文章复现流程和记录复现过程中出现的问题。 1 安装依赖环境 pip install gdown pip install transformers pip install captum pip install torch torchvision torchaudio pip install numpy pip install tqdm…

Jmeter性能测试(三)

token鉴权处理 1、添加json提取器 2、写jsonpath表达式在响应Body中提取鉴权token token&#xff1a;变量名&#xff0c;可以直接引用 $…token&#xff1a;token数据在响应中的字段名称&#xff0c;根据自己情况写就行 3、将提取出来的token添加到请求头中 重点&#xff…

基于centos7的Linux虚拟机系统安装jdk宝宝级教学

我放在opt文件夹中的soft文件夹中的Java文件夹里面 创建opt文件夹 mkdir opt 进入opt文件夹 cd opt 创建 soft文件夹 mkdir soft 进入soft文件夹 cd soft 创建Java文件夹 mkdir java 下载链接 https://www.oracle.com/java/technologies/javase/javase-jdk8-downlo…

【深度学习】Diffusion扩散模型原理解析1

1、前言 diffusion&#xff0c;这几年一直很火的模型&#xff0c;比如这段时间在网上的文生图大模型——Stable diffusion。就是以diffusion作为基底模型&#xff0c;但由于该模型与VAE那边&#xff0c;都涉及了较多了概率论知识&#xff0c;实在让人望而却步。所以&#xff0…

luceda ipkiss教程 67:修改器件端口名

如果要替换线路中的器件&#xff0c;但是要替换的器件端口名称又不一样&#xff0c;那该怎么办呢&#xff1f;去对应改线路中端口的名称太过繁琐&#xff0c;这就需要需要器件的端口名&#xff0c;如&#xff1a; 改y分束器的端口名 改了端口名称&#xff0c;线路中的器件就可…

【大学物理】东北大学-马文蔚听课笔记

4.1刚体的定轴转动_哔哩哔哩_bilibili 此笔记为课堂学习笔记~ 4.1刚体的定轴转动 基本教学要求 什么时刚体呢&#xff1f; 研究刚体运动切口 平动&#xff1a;刚体中所有的点的运动轨迹都完全相同。 转动&#xff1a;分为&#xffe5;定轴转动和非定轴转动 刚体转动的角速度…

Axure实现菜单抽屉效果

Axure是怎么实现如下效果的&#xff1f; 菜单打开和收起侧边栏菜单抽屉效果 实现效果 两级菜单&#xff0c;点击菜单收起其他菜单&#xff0c;打开当前菜单。 实现原理 单击一级菜单时&#xff0c;1&#xff09;切换当下二季菜单的显示/隐藏状态 2&#xff09;隐藏其他菜单…

MultiBoot 和 QuickBoot

目录 MultiBoot简介MultiBoot 实现方式设置 bitstream 属性使用 ICAPE2 原语WBSTAR 寄存器定义 MultiBoot 工作流程生成mcs固化文件 Tcl 指令Fallback状态寄存器MultiBoot 正常加载状态看门狗1超时状态看门狗2超时状态CRC 错误和无 DESYNC 命令IDCODE 错误状态CRC 错误状态 Wat…

0510_IO5

练习题&#xff1a; #include <stdio.h>#include <string.h>#include <stdlib.h>#include <sys/types.h>#include <unistd.h>#include <sys/stat.h>#include <fcntl.h>#include <pthread.h>#include <semaphore.h>#incl…

判断字符是否唯一——力扣

面试题 01.01. 判定字符是否唯一 已解答 简单 相关标签 相关企业 提示 实现一个算法&#xff0c;确定一个字符串 s 的所有字符是否全都不同。 示例 1&#xff1a; 输入: s "leetcode" 输出: false 示例 2&#xff1a; 输入: s "abc" 输出: true…