09线程安全

POSIX信号量

POSIX信号量分为两种,分别是POSIX无名信号量和POSIX有名信号量。这两种信号量比之前的system-V的信号量机制更加简洁。

1、POSIX有名信号量

  • 这种又名信号量的名字类似于/somexxxxx这样的字符串,前面需要一个正斜杠,这样的信号量其实是一个特殊的文件,创建成功后会被放置在系统的一个特殊虚拟文件系统/dev/shm中。不同的进程只要约定好一个相同的名字,就可以通过这种有名信号量来相互协调。
  • 有名信号量和system-V中的信号量都是属于系统资源,在进程推出后他们不会自动消失,需要用户手动删除并释放资源。

创建打开一个POSIX有名信号量:

在这里插入图片描述
对POSIX信号量进程PV操作

在这里插入图片描述

  • system-V的信号量申请和释放的个数可以超过1个资源。对于POSIX来说,每次申请和释放的资源数都是1。其中调用sem_wait()在资源为0时会阻塞等待,如果不想等待,可以用sem_trywait()来代替
    在这里插入图片描述

关闭和删除POSIX有名信号量

在这里插入图片描述
练习:
1、使用POSIX有名信号量实现进程间的内存共享操作

//send.c
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>

char * shm_init( void )
{
    // 获取key值
    int key = ftok("./" , 'X');
    // 获取SHM 的ID 
    int shm_id = shmget(key , 4096 , IPC_CREAT | 0644 );
    if (-1 == shm_id )
    {
        perror("shmget id error");
        exit(1); // 直接结束程序(退出进程)
    }
    // 映射共享内存
    char * shm_map = shmat(shm_id ,  NULL , 0 );
    if ((void *) -1 == shm_map)
    {
        perror("shm map error");
        exit(1); // 直接结束程序(退出进程)
    }
    return shm_map ;
}

int main(int argc, char const *argv[])
{
    // 先搞定共享内存并初始化
    char * shm_map = shm_init();

    // 有名信号量
    // 使用 sem_open( )来创建或者打开一个有名信号量。
    sem_t * fd_sem = sem_open("/my_sem_name", O_CREAT  , 0644 , 1); // 创建一个新的有名信号量并初始化资源为1
    while(1)
    {
        // 申请一个资源
        printf("等待申请资源中!!\n");
        sem_wait( fd_sem );
        printf("成功申请到资源!!!\n 请输入消息:\n");

        // 写入信息到共享内存中
        fgets(shm_map , 4096 , stdin);

        // 释放1个资源 
        printf("释放资源!!\n");
        sem_post(fd_sem);
    }

    // 程序退出时应当释放信号量资源

    return 0;
}
//recv.v

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include "unistd.h"

char * shm_init( void )
{
    // 获取key值
    int key = ftok("./" , 'X');

    // 获取SHM 的ID 
    int shm_id = shmget(key , 4096 , IPC_CREAT | 0644 );
    if (-1 == shm_id )
    {
        perror("shmget id error");
        exit(1); // 直接结束程序(退出进程)
    }

    // 映射共享内存
    char * shm_map = shmat(shm_id ,  NULL , 0 );
    if ((void *) -1 == shm_map)
    {
        perror("shm map error");
        exit(1); // 直接结束程序(退出进程)
    }
    return shm_map ;
}

int main(int argc, char const *argv[])
{
    // 先搞定共享内存并初始化
    char * shm_map = shm_init();

    // 有名信号量
    // 使用 sem_open( )来创建或者打开一个有名信号量。
    sem_t * fd_sem = sem_open("/my_sem_name", O_CREAT  , 0644 , 0 ); // 创建一个新的有名信号量并初始化资源为1
    while(1)
    {
        // 申请一个资源
        printf("等待申请资源中!!\n");
        sem_wait( fd_sem );
        printf("成功申请到资源!!!\n");

        // 输出共享内存的内容
        printf ("收到消息:%s\n" , shm_map);

        sleep(2);

        // 释放1个资源 
        printf("释放资源!!\n");
        sem_post(fd_sem);
    }
    return 0;
}

2、POSIX无名信号量

如果我们要解决的问题是一个线程内部的同步互斥问题,那么我们可以不需要使用POSIX有名信号量,可以使用无名信号量来达到目的。

初始化和销毁无名信号量

在这里插入图片描述

  • 无名信号量一般都用在进程内的线程间,因此pshared一般都为0。
  • 当此信号用在进程间时,必须将他定义在每个进程都能访问的位置–如共享内存当中

练习:1、使用POSIX无名信号量实现进程内的内存共享操作

#include "stdio.h"
#include <semaphore.h>
#include "unistd.h"
#include "stdlib.h"
#include "pthread.h"

//定义线程间的共享资源
sem_t sem;
//定义线程间的共享内存
char *mem_map = NULL;

void* func(void * arg)
{
    char *msg = (char *)arg;
    while(1)
    {
        //等待资源
        sem_wait(&sem);
        printf("线程收到资源为:%s\n", mem_map);
        sem_post(&sem);
        sleep(3);
    }   
}

int main()
{
    //初始化共享内存
    mem_map = malloc(32);

    //初始化无名信号量  0 表示在进程间通信   1表示初始化资源为1
    if(sem_init(&sem, 0 ,1))
    {
        printf("sem error\n");
        return -1;
    }

    //创建线程
    pthread_t pid;
    pthread_create(&pid, NULL, func, NULL);

    while(1)
    {
        sem_wait(&sem);
        fgets(mem_map, 32, stdin);
        
        sem_post(&sem);
        sleep(3);
    }
}

无名信号量是在进程内部线程之间进行同步的,因此不需要手动销毁

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值