您现在的位置是:主页 > news > 余姚网站建设哪家好/磁力王

余姚网站建设哪家好/磁力王

admin2025/5/20 2:43:14news

简介余姚网站建设哪家好,磁力王,辽阳专业建设网站,本机iis发布网站后台管理信号量信号量信号量的同步与互斥实现信号量与条件变量实现同步的区别:信号量的接口函数:信号量如何实现同步与互斥信号量实现同步信号量实现互斥使用信号量实现生产者与消费者模型(代码实现)信号量 信号量的同步与互斥实现 作用&#xff1a…

余姚网站建设哪家好,磁力王,辽阳专业建设网站,本机iis发布网站后台管理信号量信号量信号量的同步与互斥实现信号量与条件变量实现同步的区别:信号量的接口函数:信号量如何实现同步与互斥信号量实现同步信号量实现互斥使用信号量实现生产者与消费者模型(代码实现)信号量 信号量的同步与互斥实现 作用&#xff1a…

信号量

  • 信号量
      • 信号量的同步与互斥实现
      • 信号量与条件变量实现同步的区别:
      • 信号量的接口函数:
      • 信号量如何实现同步与互斥
        • 信号量实现同步
        • 信号量实现互斥
      • 使用信号量实现生产者与消费者模型(代码实现)

信号量

信号量的同步与互斥实现

作用:实现线程或进程之间的同步与互斥
本质:计数器+等待队列+等待与唤醒的功能接口
场景:用于实现进程/线程间的同步与互斥
信号量实现同步:通过自身的计数器进行资源计数,对临界资源访问之前先访问信号量,通过计数判断是否有资源能够访问,若不能访问( 计数<=0 没有资源),则等待,并且计数-1,(如果没有资源了,资源为0,等待并且计数-1,变成-1),当信号量计数器为负值,就说明有多少线程正在等待;若可以访问-计数>0,则计数-1,直接访问;其它线程生产资源促使条件满足后,则判断若计数>0,计数+1(表示没人等待,只把计数+1就可以了,说明此时已经有资源了),否则若计数<0,则唤醒一个等待队列上的线程,并计数+1.(<0说明有人在等待,计数+1,就说明等待的人少了一个)
信号量实现互斥
只需要将计数维持在0/1之间就可以实现互斥

信号量与条件变量实现同步的区别:

条件变量实现同步,没有提供给用户条件判断的功能,是需要用户自己来判断的而信号量本身有计数器,资源计数,有没有资源都是通过计数器来进行资源计数的,通过计数器,我们就能知道有没有资源,能不能访问,跟信号量最大的区别就是条件变量通过自身的计数来实现条件判断
信号量通过自身条件判断实现同步,条件变量是通过用户进行条件判断;并且条件变量使用的是外部用户的条件判断,必须是搭配互斥锁一起使用,而信号量,是通过自身的条件判断,在内部保证了操作的原子性。

信号量的接口函数:

1. 定义信号量: sem_t类型   (sem_t类型的结构体中肯定有 计数器,阻塞队列,等待与唤醒的接口)2. 初始化信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
信号量变量,(sem_t 结构体至少要有计数器,等待队列,保证计数安全内部有其它的锁)
pshared :标志位,决定了当前的信号量用与进程间还是线程间,0-线程间 ,!0-进程间
(因为是一个库,线程间用全局变量就可以完成,用于进程间(进程间相互独立),实际上申请了一块共享内存,在共享内存中包含了计数器	和共享队列)
信号量的初值(由用户根据资源而定)如果定义的是互斥锁的话,那么初始值就是1,因为互斥是由0和1控制的。
成功0  失败-14. 在访问临界资源之前,先访问信号量,判断是否访问: 计数-1
条件判断(有没有资源自给判断)+等待int sem_wait(sem_t *sem) ;//若计数<= 0, 则-1后阻塞;否则-1后立即返回  --阻塞函数trywait(sem_t *sem)-- 通过自身计数判断是否满足访问条件,不满足则立即报错返回 EINVAL
timedwait(sem_t *sem,const struct timespec* abs_timeout)-- 限制时长的阻塞,超时后报错返回 ETIMEDOUT5. 促使访问条件满足 + 1, 唤醒阻塞线程/进程
int sem_post(sem_t *sem);	//唤醒信号量等待队列上的线程 且计数+16. 销毁信号量
int sem_destroy(sem_t * sem); //销毁信号量

注意: 一个信号量只有一个等待队列,所以我们用信号量实现生产者与消费者模型的时候,需要定义两个信号量实现同步,每一个信号量需要等待在每一个不同的等待队列上。

信号量如何实现同步与互斥

信号量实现同步

厨师做饭的例子:

 sem_t _sem;void * food(){int i = 0;while(1){sem_wait(&_sem); // 没有资源则阻塞等待,有资源则-1访问资源printf("%d delicious ~~\n",i++);    }return NULL;}void* cook(){while(1){printf("cook ~~\n");sleep(1);sem_post(&_sem);  //资源+1                                                                                                       }return NULL;}
int main()
{sem_init(&_sem,0,0); //一开始没有资源,因为还没有做饭,所以没有资源,第三个参数为0。。。
}

信号量实现互斥

抢车票的例子:(伪代码)

int tickets = 100;
sem_t _sem;void* buy(void* arg)
{while(1){sem_wait(&_sem); //P操作, -1加锁, 现在初始资源为0,代表加锁状态if(tickets > 0){tickets--;sem_post(&_sem); //V操作,+1,解锁, 唤醒等待队列上的线程}else{return NULL;}}return NULL;
}int main()
{sem_init(&_sem,0,1); //第二个参数0是线程间,因为信号量实现互斥是0/1判断是否有资源,所以第三个参数资源初始值为1int  ret = pthread_create(&buyer,NULL,buy,NULL);ret = pthread_create(&seller,NULL,buy,NULL);pthread_join(buyer,NULL);pthread_join(buyer,NULL);sem_destroy(&_sem);}

使用信号量实现生产者与消费者模型(代码实现)

使用信号量实现一个线程安全的阻塞队列,模拟实现生产者与消费者模型

信号量实现生产者与消费者模型的思想
我们使用数组来实现环形的阻塞队列,通过数据空间和剩余空间的大小代表消费者和生产者线程,生产者表示现在有多少剩余空间没被使用,消费者是对数据空间的计数。

使用三个信号量实现环形阻塞队列,1个信号量实现互斥,一个用于对空闲空间进行计数 , 实现入队操作的合理性,一个用于对数据进行计数 , 实现出队操作的合理性。

注意点:

  1. 加锁保护的是临界资源
  2. 在访问是否有剩余空间或者剩余数据的时候,要先判断是否有资源,再去加锁。如果加锁了,然后再去判断是否有空闲空间,若没有则阻塞,但是这里的阻塞和条件变量的阻塞不一样,不会解锁的。你阻塞了,别人就都阻塞了
  3. 要先解锁再去唤醒消费者或者生产者线程,因为锁只保护临界资源
    1 #include <iostream>                                                                                                              2 #include <vector>3 #include <pthread.h>4 #include <semaphore.h>5 using namespace std;6 7 #define MAX_QUE 108 9 10 class RingQueue11 {12     //三个信号量实现环形阻塞队列13     //1个信号量实现互斥14     //一个用于对空闲空间进行计数 -- 实现入队操作的合理性15     //一个用于对数据进行计数 -- 实现出队操作的合理性16 private:17     vector<int> _queue; //使用数组实现环形队列18     int _capacity; // 初始化环形队列的节点数量19     int _step_read; // 当前读位置的数组下标20     int _step_write; // 当前写位置的数组下标21     sem_t _sem_lock; // 用户实现互斥的锁,信号量实现互斥的锁。22     sem_t _sem_con;  //消费者等待队列,计数器对有数据的空间进行计数23     sem_t _sem_pro; // 生产者等待队列,计数器对有空闲的空间进行计数24 public:25     RingQueue(int max_que = MAX_QUE)26         :_capacity(max_que)27          ,_queue(max_que)28          ,_step_read(0)29          ,_step_write(0)30     {31         //初始化信号量32         sem_init(&_sem_lock, 0, 1);// 第二个参数0是用在线程间的,第三个参数的1是 条件变量实现的互斥锁不能大于1 只能0或1,初始值1>33         sem_init(&_sem_con,0,0);34         sem_init(&_sem_pro,0,max_que); //生产者,初始化是节点的数量,数组有多少空节点,35         // 就可以访问多少次,生产多少数据36 37     }38     ~RingQueue()39     {40         //销毁信号量41         //42         sem_destroy(&_sem_lock);43         sem_destroy(&_sem_con);44         sem_destroy(&_sem_pro);45     }46 47     bool QueuePush(int &data)48     {49 50         //lock和_sem_lock的顺序不能变51         //如果加锁了,然后再去判断是否有空闲空间,若没有则阻塞,52         //但是这里的阻塞和条件变量的阻塞不一样,不会解锁的。你阻塞了,别人就都阻塞了53         //所以是先判断是否有资源访问,能够访问了才加锁, 加锁保护的只是临界资源的访问,54         //55         //56         //sem_post(&_lock)57         //信号量实现互斥:保证计数不大于1             
```58         //对资源访问之前先sem_wait(&lock),判断计数是否>0,大于0,才会访问,并计数-1;,否则会阻塞59         //通过0和1的计数实现互斥,访问的时候-1,计数变为0,别人就不能访问,而阻塞60         //我访问完了之后就得解锁,61         //就是再把计数器+1,唤醒别的线程也能访问,这就是sem_post62     63         //数据入队64         //1. 判断是否能够访问临界资源。判断空闲空间计数是否大于0,判断是否有空闲空间 65         sem_wait(&_sem_pro);//如果小于0.生产者等待在它的等待队列桑66         //2. 先加锁67         sem_wait(&_sem_lock);// lock的计数大大于1,当前若可以访问则-168         69         //3. 数据的入队操作70         _queue[_step_write] = data;71         _step_write = (_step_write + 1)% _capacity;72         //4. 解锁73         sem_post(&_sem_lock); // lock计数+1 此时lock为1,其他线程 可以访问互斥锁74         //5. 数据资源的空间计数+1, 唤醒消费者75         sem_post(&_sem_con); 76 77 78         return true;79     }80     bool QueuePop(int & data)81     {82         sem_wait(&_sem_pro); // 看是否有数据83         sem_wait(&_sem_lock);// 有数据则加锁保护访问数据的过程84 85         data = _queue[_step_read]; // 获取数据86         _step_read = (_step_read+1)%_capacity;87      88         sem_post(&_sem_lock);// 解锁操作89         sem_post(&_sem_pro); //取出数据,并且对空闲计数+1,唤醒消费者90 91         return true;92     }93 };94