Linux 高级编程——线程控制


线程控制:互斥与同步

概念:
    互斥 ===》在多线程中对临界资源排他性访问。

    互斥机制 ===》互斥锁  ===》保证临界资源的 访问控制。

    pthread_mutex_t   mutex;
    互斥锁类型        互斥锁变量 内核对象

    框架:
     定义互斥锁 ==》初始化锁 ==》加锁 ==》解锁 ==》销毁
        ****                      ***      ***

     1、定义:
        pthread_mutex_t   mutex;

     2、初始化锁
        int pthread_mutex_init(
            pthread_mutex_t *mutex,
            const pthread_mutexattr_t *attr);
        功能:将已经定义好的互斥锁初始化。
        参数:mutex 要初始化的互斥锁
              atrr  初始化的值,一般是NULL表示默认锁
        返回值:成功 0
                失败 非零
     3、加锁:
        int pthread_mutex_lock(pthread_mutex_t *mutex);
        功能:用指定的互斥锁开始加锁代码
              加锁后的代码到解锁部分的代码属于原子操作
              在加锁期间其他进程/线程都不能操作该部分代码
              如果该函数在执行的时候,mutex已经被其他部分
              使用则代码阻塞。

        参数: mutex 用来给代码加锁的互斥锁
        返回值:成功 0
                失败 非零

     4、解锁
        int pthread_mutex_unlock(pthread_mutex_t *mutex);
        功能:将指定的互斥锁解锁。
              解锁之后代码不再排他访问,一般加锁解锁同时出现。
        参数:用来解锁的互斥锁
        返回值:成功 0
                失败 非零

     5、销毁
         int pthread_mutex_destroy(pthread_mutex_t *mutex);
         功能:使用互斥锁完毕后需要销毁互斥锁
         参数:mutex 要销毁的互斥锁
         返回值:成功  0
                 失败  非零

     6、trylock
        int pthread_mutex_trylock(pthread_mutex_t *mutex);
        功能:类似加锁函数效果,唯一区别就是不阻塞。
        参数:mutex 用来加锁的互斥锁
        返回值:成功 0
                失败 非零
                E_AGAIN

注意:

被互斥锁包含的地方尽量简短

锁的位置就是要小,尽可能做到并发

互斥锁中不会吧延迟函数锁上

上了锁的地方一定要检查某些地方解锁了没。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>int a = 0;
pthread_mutex_t mutex;
void* th(void* arg)
{// pthread_mutex_lock(&mutex);int i =5000;while(i--){pthread_mutex_lock(&mutex);int tmp = a;printf("a is %d\n",tmp+1);a = tmp+1;pthread_mutex_unlock(&mutex); }// pthread_mutex_unlock(&mutex); return NULL;
}int main(int argc, char *argv[])
{pthread_t tid1,tid2;pthread_mutex_init(&mutex,NULL);pthread_create(&tid1,NULL,th,NULL);pthread_create(&tid2,NULL,th,NULL);pthread_join(tid1,NULL);pthread_join(tid2,NULL);pthread_mutex_destroy(&mutex);return 0;
}

十个人来银行办业务,
    int a  = 0 ;
    
    while(5000--)
    {
        int temp = a;
        printf("%d",temp+1);
        a =temp+1;
    }

        get win   sleep(rand()%5);
        get win
        get win 
        leave 
         
    10线程,
    count = 3,
    th()
    {
    while(1)
    {
        if( cout >0 )
        {    
            //you
            printf("get totile\n");
            cout--;
            break;
        }
        else
        {
            //sleep(1);
        }
    }
    rand()%5
        sleep(5)
        printf("release totile\n")
        coutt++;
    }
    main()
    {
        pthread_create*10;
        pthread_join();*10
    }
 

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

               
                
    练习:
        设计一个多线程程序,共享同一块字符数组,分别向该
        数组中写入字符串,保证某一时刻只能有一个线程在操作
        该数组,使用堆区/栈区互斥锁来保证排他性访问。
        
    练习2:
    使用互斥锁完成如下功能:
    设计多线程程序,同时对指定的文件进行写操作
    要求每个线程写入的信息不能覆盖和交叉。
    文件中的数据要是输入信息的总和不能丢失。
   
  

 线程的同步 ===》同步 
  ===》有一定先后顺序的对资源的排他性访问。

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

    linux下的线程同步  ===》信号量机制 ===》semaphore.h   posix 
    sem_open();

信号量是内核中的操作,
    信号量的分类:
    1、无名信号量 ==》线程间通信
    2、有名信号量 ==》进程间通bn

    

框架:
    信号量的定义 ===》信号量的初始化 ==》信号量的PV操作
    ===》信号量的销毁。

    semaphore 
    1、信号量的定义 :
       sem_t            sem;
       信号量的类型     信号量的变量

    2、信号量的初始化
        int sem_init(sem_t *sem, int pshared, unsigned int value);
        功能:将已经定义好的信号量赋值。
        参数:sem 要初始化的信号量
              pshared = 0 ;表示线程间使用信号量
                      !=0 ;表示进程间使用信号量
              value 信号量的初始值,一般无名信号量
              都是二值信号量,0 1 
              0 表示红灯,进程暂停阻塞
              1 表示绿灯,进程可以通过执行
        返回值:成功  0
                失败  -1;
    3、信号量的PV 操作
       P ===》申请资源===》申请一个二值信号量 
       V ===》释放资源===》释放一个二值信号量

二值信号量;0或者1;

       P操作对应函数 ==》sem_wait();
       V操作对应函数 ==》sem_post();

需要进行交叉操作。申请自己的,释放对方的。

    int sem_wait(sem_t *sem);
    功能:判断当前sem信号量是否有资源可用。
          如果sem有资源(==1),则申请该资源,程序继续运行
          如果sem没有资源(==0),则线程阻塞等待,一旦有资源
          则自动申请资源并继续运行程序。

          注意:sem 申请资源后会自动执行 sem = sem - 1;
    参数:sem 要判断的信号量资源
    返回值:成功 0 
            失败 -1
        
    int sem_post(sem_t *sem);
    功能:函数可以将指定的sem信号量资源释放
          并默认执行,sem = sem+1;
          线程在该函数上不会阻塞。
    参数:sem 要释放资源的信号量
    返回值:成功 0
            失败 -1;

    4、信号量的销毁
       int sem_destroy(sem_t *sem);
       功能:使用完毕将指定的信号量销毁
       参数:sem要销毁的信号量
       返回值:成功 0
                失败  -1;

  实现先打印hello,紧接着打印world

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
sem_t sem_H,sem_W;
void* th1(void* arg)
{int i =10;while(i--){sem_wait(&sem_H);printf("hello");fflush(stdout);sem_post(&sem_W);}return NULL;
}
void* th2(void* arg)
{int i =10;while(i--){sem_wait(&sem_W);printf(",world\n");sleep(1);sem_post(&sem_H);}return NULL;
}int main(int argc, char *argv[])
{pthread_t tid1,tid2;sem_init(&sem_H,0,1);sem_init(&sem_W,0,0);pthread_create(&tid1,NULL,th1,NULL);pthread_create(&tid2,NULL,th2,NULL);pthread_join(tid1,NULL);pthread_join(tid2,NULL);sem_destroy(&sem_H);sem_destroy(&sem_W);return 0;
}

 实现银行示例用信号量。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
sem_t sem_WIN;
void* th(void* arg)
{sem_wait(&sem_WIN);printf("get win\n");sleep(rand()%5);printf("relese win\n");sem_post(&sem_WIN);return NULL;
}int main(int argc, char *argv[])
{srand(time(NULL));pthread_t tid[10];int i = 0 ;//计数信号量sem_init(&sem_WIN,0,3);for(i = 0 ;i<10;i++){pthread_create(&tid[i],NULL,th,NULL);}for(i = 0 ;i<10;i++){pthread_join(tid[i],NULL);}sem_destroy(&sem_WIN);return 0;
}


    pthread_attr_t attr;
    int pthread_attr_init(pthread_attr_t *attr);

  int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachst
ate)
功能:设置线程为分离的属性,线程自己在消亡的时候,释放相关的资源。
    attr,出参,由该函数填充。
    detachstate
        PTHREAD_CREATE_DETACHED:
        设置分离属性的标记
        PTHREAD_CREATE_JOINABLE:
        设置关联的属性:
        
        返回  0 成功
              >0 失败,以及错误号
              
              
              
    int pthread_detach(pthread_t thread);
    功能:设置线程为分离的属性,线程自己在消亡的时候,释放相关的资源。
    参数:thread,需要设置分离属性的tid
    

        返回  0 成功
              >0 失败,以及错误号
              
    pthread_yield();usleep(1000);
    功能:本线程放弃cpu的调度。
    
    
    
    
    
    
    abcabcabcabc
    AAABBBCCC
    
    3~5, 100
    
    
    6线程, mallco 3 
    int mem[3];//1. 0,
    sem_mem 3;
    int mymall0c()
    {
        wait(sem_mem);
        for(i == 0;i<3;i++)
        if( m[i] == 1) 
        return i;
    }
    void myfree(id)
    {
        mem[id]=1;
        sem_post(sem_mem);
    }
    void* th(void* age)    
    {
        int id = mymalloc();
        printf(我拿到di i 个空间);
        sleep(rand()%5);
        
        myfree(id);
        
    
    
    }
    
    main()
    {
    
        pthread_create();//6
        ptread_join();//6
    }
    
    
    
    1,服务员, 3桌子
    
                    20
    1,前台,报菜单,  name,号。
    当前叫号,10,
    sturct()
    {
        name【100】,
        total_num;
        call_num;
        mutex_lock;
    }
    record;
    customer)()
    {
        printf("eat...");
        sleep();
        printf("out");
        sem_post();
    }
    
    waiter()
    {
        while(1)
        {
        wait(desk)    8      10
        if ()//有人  call < total
        pthread_create(aa);
        else
        {
            
        }
    }
    }
    main()
    {
    
        pthread_create();//wait
        
        while()
        fgets,name, total++;
        
    }
    
    

互斥锁(Mutex)和信号量(Semaphore)都是多线程编程中用于实现资源共享和线程同步的机制,但它们在应用场景、实现方式和性能特点上有所不同。以下是它们之间的区别和相同点的详细解释:

区别

  1. 应用场景
    • 互斥锁主要用于线程互斥,确保同一时刻只有一个线程能访问特定的资源,防止资源被多个线程同时访问。
    • 信号量主要用于线程同步,控制对共享资源的访问许可。当资源可用时,允许线程继续操作;当资源被占用时,线程则阻塞直到资源变得可用。
  2. 实现方式
    • 互斥锁的值通常只能为0或1,表示资源是否被锁定。当一个线程获取互斥锁时,其他试图访问该资源的线程将被阻塞,直到锁被释放。
    • 信号量的值可以是任意非负整数,表示可用资源的数量。当信号量的值大于0时,表示有可用资源,线程可以继续操作;当信号量的值为0时,表示没有可用资源,线程需要阻塞直到资源变得可用。
  3. 性能特点
    • 互斥锁在锁定资源时,所有试图访问该资源的线程都会被阻塞,直到资源被解锁。它的实现相对简单,效率较高,但在锁争用激烈的场景下,可能会带来较大的性能开销。
    • 信号量在资源被锁定时,允许其他线程继续执行某些任务,直到资源被释放。因此,它更适用于需要等待时间较长的临界区。同时,信号量还可以用于进程间通信,而互斥锁仅用于线程间通信。
  4. 所有权特性
    • 互斥锁具有明确的所有权特性,即同一时间只能有一个任务持有互斥锁,而且只有这个任务可以对互斥锁进行解锁。
    • 信号量并没有明确的所有权特性,一个进程阻塞在某个信号量上时,它无法知道自己阻塞在哪个进程(线程)之上。

相同点

  1. 重要性:对于线程来说,互斥锁和信号量都是非常重要的概念,它们都可以避免死锁或者读脏数据。
  2. 目的:两者的主要目的都是为了保护共享资源,确保线程在访问这些资源时的正确性和一致性。
  3. 基本机制:它们都采用了某种形式的锁定机制来防止多个线程同时访问共享资源。

总结来说,互斥锁和信号量在多线程编程中各有其应用场景和优缺点。在选择使用哪种机制时,需要根据具体的需求和场景进行权衡和选择。

    
    
    
    产生死锁的原因主要是:

(1) 因为系统资源不足。
(2) 进程运行推进的顺序不合适。
(3) 资源分配不当等。
如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则
就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。
产生死锁的四个必要条件
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。优先级问题
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
 

死锁的原因
 

系统资源不足:当系统中的资源不足以满足所有进程的需求时,进程之间可能会因为争夺资源而产生死锁。
进程运行推进的顺序不合适:即使系统资源充足,如果进程请求资源的顺序不当,也可能导致死锁。例如,进程A拥有资源1并请求资源2,而进程B拥有资源2并请求资源1,此时就可能出现循环等待,导致死锁。
资源分配不当:资源分配策略的不当也可能导致死锁。例如,某些资源分配算法可能倾向于将资源分配给某些进程,而忽略其他进程的需求,从而增加了死锁的风险。

死锁的四个必要条件


互斥条件(Mutual Exclusion):一个资源每次只能被一个进程使用。即资源是独占的,当一个进程持有一个资源时,其他进程不能同时访问它。


请求与保持条件(Hold and Wait):一个进程因请求资源而阻塞时,对已获得的资源保持不放。即进程已经持有一些资源,但又提出了新的资源请求,而该资源又被其他进程占有,此时请求进程阻塞,但又对自己持有的资源保持不放。


不剥夺条件(No Preemption):进程已获得的资源,在末使用完之前,不能强行剥夺。即资源只能由获得它的进程自己来释放,进程之间不能相互剥夺对方的资源。


循环等待条件(Circular Wait):若干进程之间形成一种头尾相接的循环等待资源关系。即存在一个等待资源的循环链,链中每个进程已获得的资源同时被链中下一个进程所请求。


只有当这四个条件同时成立时,系统中才可能发生死锁。因此,在设计和实现并发系统时,通常会采取一些策略来避免或解决死锁问题,如预防策略(破坏四个条件中的至少一个)、避免策略(在资源分配之前判断是否会导致死锁)、检测和恢复策略(允许死锁发生,但系统能够检测到死锁并采取相应的措施来恢复)。

  

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

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

相关文章

前端——在本地搭建Vue单页应用

目录 1、安装最新node.js 2、打开命令行窗口 3、进入要保存项目的目录下 4、安装 Vue CLI 5、创建新项目&#xff0c;选择功能 5.1 新建项目 5.2 Please pick a preset 5.3 Check the features needed for your project 5.4 Choose a version of Vue.js 5.5 Use hist…

优化数据库字段使用位运算-php语言示例

背景&#xff1a;一个会员有三个状态&#xff0c;A、B、C&#xff0c;其中一个人可以为 A、B、C、AB&#xff1b;之前数据表结构加了三个字段is_a、is_b、is_c; 本人实在不想这样粗糙的实现需求&#xff0c;遂决定用位运算优化。 上代码&#xff1a; 位运算可以用来处理状态值…

探索SOLIDWORKS 2024设计增强功能

随着技术的不断进步和市场的日益竞争&#xff0c;工程设计和制造行业对于快捷、准确和创新的工具需求日益增长。SOLIDWORKS作为3D CAD设计软件&#xff0c;一直致力于为用户提供更强大、更便捷的设计工具。SOLIDWORKS 2024的发布&#xff0c;再次证明了其在设计增强功能方面的持…

使用 Amazon Bedrock Converse API 简化大语言模型交互

本文将介绍如何使用 Amazon Bedrock 最新推出的 Converse API&#xff0c;来简化与各种大型语言模型的交互。该 API 提供了一致的接口&#xff0c;可以无缝调用各种大型模型&#xff0c;从而消除了需要自己编写复杂辅助功能函数的重复性工作。文中示例将展示它相比于以前针对每…

批量文件名修改软件:一键解决同一编码多型号文件分类与命名难题,高效管理文件

在数字化时代&#xff0c;图片文件已经成为我们工作中不可或缺的一部分。然而&#xff0c;当面对成百上千个同一编码下不同型号的图片文件时&#xff0c;如何快速、准确地进行分类和命名&#xff0c;成为了许多职场人士头疼的问题。现在&#xff0c;我们为您带来了一款神奇的批…

MyBatisPlus 基础数据表的增删改查 入门 简单查询

MyBatisPlus MyBatisPlus&#xff08;简称MP&#xff09;是一个基于MyBatis的增强工具库&#xff0c;简化了MyBatis的开发&#xff0c;提供了很多实用的功能和特性&#xff0c;如自动生成SQL、通用CRUD操作、分页插件、条件构造器、代码生成器等。它不仅简化了开发过程&#x…

优盘有盘符显示0字节:故障解析与数据恢复策略

一、优盘有盘符显示0字节现象描述 在使用优盘的过程中&#xff0c;我们有时会遇到一种令人困惑的情况&#xff1a;插入优盘后&#xff0c;电脑能正常识别到优盘的盘符&#xff0c;但当我们尝试访问其中的数据时&#xff0c;却发现优盘的容量显示为0字节&#xff0c;无法读取或…

Sui创始团队在竞速环节中的快问快答

在Sui Basecamp活动期间&#xff0c;Sui区块链的最初贡献者在Oracle红牛赛车模拟器上展示了他们的技术能力&#xff0c;在驾驶圈时回答了有关Sui的问题。 Evan Cheng&#xff08;又名Revvin’ Evan&#xff09;在解释Mysticeti创下区块链最终性记录的同时保持着他的驾驶线路。…

Java | Leetcode Java题解之第200题岛屿数量

题目&#xff1a; 题解&#xff1a; class Solution {void dfs(char[][] grid, int r, int c) {int nr grid.length;int nc grid[0].length;if (r < 0 || c < 0 || r > nr || c > nc || grid[r][c] 0) {return;}grid[r][c] 0;dfs(grid, r - 1, c);dfs(grid, r…

go Channel原理 (三)

Channel 设计原理 不要通过共享内存的方式进行通信&#xff0c;而是应该通过通信的方式共享内存。 在主流编程语言中&#xff0c;多个线程传递数据的方式一般都是共享内存。 Go 可以使用共享内存加互斥锁进行通信&#xff0c;同时也提供了一种不同的并发模型&#xff0c;即通…

mysql8.0.19安装zip版本

下载地址https://downloads.mysql.com/archives/community/ 下载版本 下载后解压&#xff0c;不包括data 和my.ini文件。其中data 文件是自动生成的【mysqld --initialize --console】&#xff0c;my.ini需要自己编写设置。 新建my.ini文件 需要自己设置 basedirG:\soft\mysql…

内网服务器时间校正

新购买的云服务器发现内网机器和可以访问外网的机器时间慢了三分钟&#xff0c;导致有些访问会报错&#xff0c;那么我们配置一下ntp校正一下时间。外网配置起来比较简单&#xff0c;直接下载ntp执行校正命令即可。 比当前时间慢了三分钟 注意当前服务器是可以访问外网的机器这…

【gitee使用教程】(创建项目仓库并上传代码简易版)

gitee使用教程&#xff0c;创建项目仓库并上传代码简易版 1.在码云上创建一个仓库2.将代码克隆到本地1.复制仓库地址2.找到你想要放置的文件位置&#xff0c;右键点击更多选项&#xff0c;选择Git Clone3.将复制的仓库地址填入URL 3. IDEA结合GIT和Gitee的简单使用idea需要识别…

【python】最新版抖音s逆向拿到数据,非常详细教程(附完整代码)

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全…

Excel 宏录制与VBA编程 ——VBA编程技巧篇一 (Union方法、Resize方法、Cells方法、UseSelect方法、With用法)

Uniom方法 使用Union方法可以将多个非连续区域连接起来成为一个区域&#xff0c;从而可以实现对多个非连续区域一起进行操作。 Resize方法 使用Range对象的Resize属性调整指定区域的大小&#xff0c;并返回调整大小后的单元格区域。 Cells方法 Cells属性返回一个Range对象。 Us…

Domino应用中的HTML5

大家好&#xff0c;才是真的好。 在xpages多年不见有效更新&#xff0c;前景不明的时候&#xff0c;Domino传统Web应用开发方式还是受到了应有的青睐。毕竟&#xff0c;在Nomad Web时代&#xff0c;连最传统的Notes CS原生应用也突然焕发了勃勃生机一样。 但&#xff0c;对有…

ARP 原理详解 一

ARP 原理 ARP&#xff08;Address Resolution Protocol&#xff09;地址解析协议&#xff0c;是根据 IP 地址获取物理地址的一个 TCP/IP 协议。 OSI 网络七层模型中&#xff0c;IP 地址在 OSI 模型第三层&#xff0c;MAC 地址在第二层&#xff0c;彼此不直接通信。 在通过以…

性能测试中的场景设计和测试执行

假设一个内部系统要求响应时间在 3s 以内&#xff0c;支持最大用户数为4万。根据二八原则&#xff0c;80%用户在20%时间使用系统(4w80%)/(24h20%)≈1.9点击/秒。并发数TPS&#xff08;运行时间思考时间&#xff09;1.9&#xff08;30.50.330.50.30.53&#xff09;21。 注意&am…

Flutter循序渐进==>数据结构(列表、映射和集合)和错误处理

导言 填鸭似的教育确实不行&#xff0c;我高中时学过集合&#xff0c;不知道有什么用&#xff0c;毫无兴趣&#xff0c;等到我学了一门编程语言后&#xff0c;才发现集合真的很有用&#xff1b;可以去重&#xff0c;可以看你有我没有的&#xff0c;可以看我有你没有的&#xf…

毫米波雷达深度学习技术-1.7训练一个神经网络

1.7 训练一个神经网络 对于训练神经网络&#xff0c;有两个步骤&#xff0c;即前向传递和误差反向传播。 1.7.1 前向传播和反向传播 在前向传递中&#xff0c;输入被馈送到模型并与权重向量相乘&#xff0c;并为每一层添加偏差以计算模型的输出。密集层或全连接层第l层的输入、…