【PTHREAD】线程互斥与同步之信号量

信号量广泛用于进程或线程建的同步和互斥,信号量本质上是一个非负整数计数器,它被用来控制对公共资源的访问。编程时,可根据操作信号量值的结果判断是否对公共资源具有访问权限,当信号量值大于0时,则可以访问,否则将阻塞。PV原语是对信号量的操作,一次P操作是信号量减1,一次V操作使信号量加1

1 信号量类型

#if __WORDSIZE == 64
# define __SIZEOF_SEM_T	32
#else
# define __SIZEOF_SEM_T	16
#endif

typedef union
{
  char __size[__SIZEOF_SEM_T];
  long int __align;
} sem_t;

2 初始化信号量

int sem_init(sem_t *sem, int pshared, unsigned int value);
  • 初始化一个无名信号量
  • 参数pshared指示该信号量是在一个进程内的所有线程共享还是在进程间共享
    • 如果该值为0,信号量被一个进程内的所有线程共享
    • 如果该值非0,信号量在进程间共享
  • 参数value指示信号量额初始值
  • 初始化一个已经初始化的信号量,将导致不确定性行为

3 销毁信号量

int sem_destroy(sem_t *sem);
  • 销毁一个无名信号量
  • 仅通过sem_init初始化的信号量,使用该函数进行销毁
  • 销毁一个正在被其他线程或进程使用的信号量,将导致不确定性行为
  • 销毁一个已销毁的信号量,将导致不确定性行为
  • 一个已销毁的信号量,可以被重新初始化

4 信号量P操作

int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
  • sem_wait

    使信号量执行一次P操作

    • 如果信号量的当前值大于零,则将信号量减一后,立即返回
    • 如果信号量的当前值等于零,则阻塞知道信号量变为可执行P操作或者一个信号处理中断被调用
  • sem_trywait

    除了当前信号量不可执行P操作时,该函数直接返回,且将errno设置为EAGAIN外,与sem_wait相同

  • sem_timedwait

    除了当前信号量不可执行P操作时,该函数将等待参数abs_timeout指定的时间外,与sem_wait相同。

    • 如果在等待时间内,能够执行P操作,则正常返回
    • 如果在等待时间内,不能执行P操作,则调用返回,且将errno设置为ETIMEDOUT

5 信号量V操作

int sem_post(sem_t *sem);
  • 将信号量的值加1

6 案例:信号量之线程互斥

注意

  • 信号量用于互斥:不管多少个任务互斥,只需要一个信号量。
  • 对于每一个线程中的任务,都是先执行P操作(sem_*wait),后执行V操作(sem_post)
  • 任务(线程)执行的先后顺序是不确定的

要求

  • 任务(线程)一连续输出0-4
  • 任务(线程)二连续输出10-14
  • 任务(线程)三连续输出20-24
  • 任务(线程)的执行顺序无要求
  • 源码

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <semaphore.h>
    
    sem_t sem;
    void *start_routine_01(void *ptr)
    {
        sem_wait(&sem);
        for (size_t i = 0; i < 5; i++)
        {
            printf("读线程(%lu)获取全局变量当前值(%lu)\n", pthread_self(), i);
            sleep(1);
        }
        sem_post(&sem);
    
        return (void *)NULL;
    }
    
    void *start_routine_02(void *ptr)
    {
        sem_wait(&sem);
        for (size_t i = 10; i < 15; i++)
        {
            printf("读线程(%lu)获取全局变量当前值(%lu)\n", pthread_self(), i);
            sleep(1);
        }
        sem_post(&sem);
    
        return (void *)NULL;
    }
    
    void *start_routine_03(void *ptr)
    {
        sem_wait(&sem);
        for (size_t i = 20; i < 25; i++)
        {
            printf("读线程(%lu)获取全局变量当前值(%lu)\n", pthread_self(), i);
            sleep(1);
        }
        sem_post(&sem);
    
        return (void *)NULL;
    }
    
    int main(int argc, char const *argv[])
    {
        sem_init(&sem, 0, 1);
    
        pthread_t thread_id_01, thread_id_02, thread_id_03;
        pthread_create(&thread_id_01, NULL, start_routine_01, NULL);
        pthread_create(&thread_id_02, NULL, start_routine_02, NULL);
        pthread_create(&thread_id_03, NULL, start_routine_03, NULL);
    
        pthread_join(thread_id_01, NULL);
        pthread_join(thread_id_02, NULL);
        pthread_join(thread_id_03, NULL);
    
        sem_destroy(&sem);
        exit(EXIT_SUCCESS);
    }
    
  • 输出

    读线程(139881171838720)获取全局变量当前值(0)
    读线程(139881171838720)获取全局变量当前值(1)
    读线程(139881171838720)获取全局变量当前值(2)
    读线程(139881171838720)获取全局变量当前值(3)
    读线程(139881171838720)获取全局变量当前值(4)
    读线程(139881163446016)获取全局变量当前值(10)
    读线程(139881163446016)获取全局变量当前值(11)
    读线程(139881163446016)获取全局变量当前值(12)
    读线程(139881163446016)获取全局变量当前值(13)
    读线程(139881163446016)获取全局变量当前值(14)
    读线程(139881155053312)获取全局变量当前值(20)
    读线程(139881155053312)获取全局变量当前值(21)
    读线程(139881155053312)获取全局变量当前值(22)
    读线程(139881155053312)获取全局变量当前值(23)
    读线程(139881155053312)获取全局变量当前值(24)

7 信号量之线程同步

注意

  • 有多少个任务,就需要多少个信号量
  • 初始化时,最先执行的任务对应的信号量设置为1,其余信号量设置为0
  • 在每个任务(线程)中,先P操作(sem_*wait)自己,后V操作(sem_post)下一个任务

要求

  • 任务(线程)一连续输出0-4
  • 任务(线程)二连续输出10-14
  • 任务(线程)三连续输出20-24
  • 任务(线程)的执行顺序必须是:
    • 任务(线程)一
    • 任务(线程)二
    • 任务(线程)三
  • 源码

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <semaphore.h>
    
    sem_t sem_01, sem_02, sem_03;
    
    void *start_routine_01(void *ptr)
    {
        sem_wait(&sem_01);
        for (size_t i = 0; i < 5; i++)
        {
            printf("读线程(%lu)获取全局变量当前值(%lu)\n", pthread_self(), i);
            sleep(1);
        }
        sem_post(&sem_02);
    
        return (void *)NULL;
    }
    
    void *start_routine_02(void *ptr)
    {
        sem_wait(&sem_02);
        for (size_t i = 10; i < 15; i++)
        {
            printf("读线程(%lu)获取全局变量当前值(%lu)\n", pthread_self(), i);
            sleep(1);
        }
        sem_post(&sem_03);
    
        return (void *)NULL;
    }
    
    void *start_routine_03(void *ptr)
    {
        sem_wait(&sem_03);
        for (size_t i = 20; i < 25; i++)
        {
            printf("读线程(%lu)获取全局变量当前值(%lu)\n", pthread_self(), i);
            sleep(1);
        }
        sem_post(&sem_01);
    
        return (void *)NULL;
    }
    
    int main(int argc, char const *argv[])
    {
        sem_init(&sem_01, 0, 1);
        sem_init(&sem_02, 0, 0);
        sem_init(&sem_03, 0, 0);
    
        pthread_t thread_id_01, thread_id_02, thread_id_03;
        pthread_create(&thread_id_01, NULL, start_routine_01, NULL);
        pthread_create(&thread_id_02, NULL, start_routine_02, NULL);
        pthread_create(&thread_id_03, NULL, start_routine_03, NULL);
    
        pthread_join(thread_id_01, NULL);
        pthread_join(thread_id_02, NULL);
        pthread_join(thread_id_03, NULL);
    
        sem_destroy(&sem_01);
        sem_destroy(&sem_02);
        sem_destroy(&sem_03);
        exit(EXIT_SUCCESS);
    }
    
  • 输出

    读线程(140602965497600)获取全局变量当前值(0)
    读线程(140602965497600)获取全局变量当前值(1)
    读线程(140602965497600)获取全局变量当前值(2)
    读线程(140602965497600)获取全局变量当前值(3)
    读线程(140602965497600)获取全局变量当前值(4)
    读线程(140602957104896)获取全局变量当前值(10)
    读线程(140602957104896)获取全局变量当前值(11)
    读线程(140602957104896)获取全局变量当前值(12)
    读线程(140602957104896)获取全局变量当前值(13)
    读线程(140602957104896)获取全局变量当前值(14)
    读线程(140602948712192)获取全局变量当前值(20)
    读线程(140602948712192)获取全局变量当前值(21)
    读线程(140602948712192)获取全局变量当前值(22)
    读线程(140602948712192)获取全局变量当前值(23)
    读线程(140602948712192)获取全局变量当前值(24)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhy29563

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值