线程的互斥与同步机制——互斥锁、条件变量、无名信号量

本文主要内容

  • 互斥锁:主要解决互斥问题,确保共享资源不冲突
  • 条件变量、无名信号量:主要解决同步问题,主要协调线程的执行顺序

一、互斥锁

(一)定义与作用

互斥锁(Mutex,即Mutual Exclusion Lock)是并发编程中用于保护 "共享资源" 的同步工具,本质是一把"独占钥匙"。

为什么需要互斥锁?当多个线程同时读写共享资源时,会出现 "竞态条件",导致数据错误;互斥锁能强制线程 "排队访问" 资源,避免竞争。

核心作用:保证 "同一时刻,只有一个线程能持有锁并访问共享资源 ",实现 "互斥访问"。

(二)相关函数

pthread_mutex_init函数

所需头文件:#include <pthread.h>

原型:int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); 动态初始化互斥锁 ;

restrict告诉编译器此指针指向的内存区域是唯一访问路径(只是为了编译器方便优化)

参数:

  • mutex指定要进行初始化的互斥锁变量
  • attr:设置互斥锁的属性,若传NULL使用系统默认属性
    pthread_mutex_t = PTHREAD_MUTEX_INITIALIZER; 静态初始化互斥锁(不会错误检查)

返回值:成功则返回0;失败则返回非0错误码。

cpp 复制代码
    #if 1
    //动态初始化互斥锁(常用)
    //动态初始化互斥锁会检查错误,若失败则返回错误码
    pthread_mutex_t mutex;
    int ret_mutex_init=0;
    if(0 != (ret_mutex_init = pthread_mutex_init(&mutex,NULL))){
        fprintf(stderr,"动态初始化互斥锁失败:%s\n",strerror(ret_mutex_init));
        return -1;
    }
    printf("动态初始化互斥锁成功\n");
    #else
    //静态初始化互斥锁
    //静态初始化互斥锁不会检查错误,所以不需要使用pthread_mutex_init()函数
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    #endif

pthread_mutex_lock函数

所需头文件:#include <pthread.h>

原型:int pthread_mutex_lock(pthread_mutex_t *mutex); 用于获取互斥锁;(加锁)

当互斥锁已被其他线程获取时,调用该函数的线程会被阻塞,直到互斥锁可用并成功获取。
== 同一时刻只能有一个线程使用共享资源,当线程没有解锁时,其他线程无法使用共享资源

参数mutex:互斥锁

返回值:成功则返回0;失败则返回非0错误码。

cpp 复制代码
    //获取互斥锁
    int ret_lock=0;
    if(0 != (ret_lock = pthread_mutex_lock(&mutex))){
        fprintf(stderr,"线程1使用阻塞方式获取互斥锁失败:%s\n",strerror(ret_lock));
        //return -1;//针对线程入口函数返回值为void*类型,所以不能使用return -1
        return NULL;
    }
    printf("线程1使用阻塞方式获取互斥锁成功\n");

pthread_mutex_trylock函数

所需头文件:#include <pthread.h>

原型:int pthread_mutex_trylock(pthread_mutex_t *mutex); 用于获取互斥锁;

当互斥锁已被其他线程获取时,调用该函数的线程不会被阻塞,而是立即返回错误;
== 同一时刻只能有一个线程使用共享资源,当线程没有解锁时,其他线程立即返回错误。

参数mutex:互斥锁

返回值:成功则返回0;失败则返回非0错误码。

cpp 复制代码
    //非阻塞方式加锁
    int ret_lock=0;
    if(0 != (ret_lock = pthread_mutex_trylock(&mutex))){
        fprintf(stderr,"线程2使用非阻塞方式获取互斥锁失败:%s\n",strerror(ret_lock));
        //return -1;//针对线程入口函数返回值为void*类型,所以不能使用return -1
        return NULL;
    }
    printf("线程2使用非阻塞方式获取互斥锁成功\n");

pthread_mutex_unlock函数

所需头文件:#include <pthread.h>

原型:int pthread_mutex_unlock(pthread_mutex_t *mutex); 解锁一般使用时,只有加锁的线程可以解锁

参数mutex:互斥锁

返回值:成功则返回0;失败则返回非0错误码。

cpp 复制代码
        //解锁
        if(0 != (ret_unlock = pthread_mutex_unlock(&mutex))){
            fprintf(stderr,"2-线程1解锁失败:%s\n",strerror(ret_lock));
            return NULL;
        }
        printf("2-线程1解锁成功\n");

pthread_mutex_destroy函数

所需头文件:#include <pthread.h>

原型:int pthread_mutex_destroy(pthread_mutex_t *mutex); 销毁互斥锁(确保互斥已经解开且不再有任何线程使用,否则销毁互斥锁可能失败)

参数mutex:互斥锁

返回值:成功则返回0;失败则返回非0错误码。

cpp 复制代码
    //销毁互斥锁
    int ret_destroy=0;
    if(0 != (ret_destroy = pthread_mutex_destroy(&mutex))){
        fprintf(stderr,"互斥锁销毁失败:%s\n",strerror(ret_join2));
        return -1;
    }
    printf("互斥锁销毁成功\n");

(三)完整代码

cpp 复制代码
//在没有互斥锁的情况下,多线程可能会出现竞态条件,也可能不会出现竞态条件
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
int num=1000;//全局变量
pthread_mutex_t mutex;//全局

//线程1入口函数
void *thread1(void *arg){
    int ret_unlock=0;
    while(1){
        //获取互斥锁
        int ret_lock=0;
        if(0 != (ret_lock = pthread_mutex_lock(&mutex))){
            fprintf(stderr,"线程1使用阻塞方式获取互斥锁失败:%s\n",strerror(ret_lock));
            //return -1;//针对线程入口函数返回值为void*类型,所以不能使用return -1
            return NULL;
        }
        printf("线程1使用阻塞方式获取互斥锁成功\n");

        //操作
        if(num>100){
            num-=100;
            printf("线程1将num-100,num=%d\n",num);
        }
        else{
            printf("线程1发现num<100,不再执行-100\n");
            //解锁
            if(0 != (ret_unlock = pthread_mutex_unlock(&mutex))){
                fprintf(stderr,"1-线程1解锁失败:%s\n",strerror(ret_lock));
                return NULL;
            }
            printf("1-线程1解锁成功\n");
            break;
        }
        //解锁
        if(0 != (ret_unlock = pthread_mutex_unlock(&mutex))){
            fprintf(stderr,"2-线程1解锁失败:%s\n",strerror(ret_lock));
            return NULL;
        }
        printf("2-线程1解锁成功\n");

        sleep(1);//目的:线程1与线程2进行交替执行
    }
    return NULL;
}

//线程2入口函数
void *thread2(void *arg){
    int ret_unlock=0;
    while(1){
        //获取互斥锁
        #if 1
        //针对互斥锁,同一时刻只能有一个线程获得互斥锁
        //如果此线程不释放互斥锁,那其他线程需要阻塞等待 直到此线程释放互斥锁
        int ret_lock=0;
        if(0 != (ret_lock = pthread_mutex_lock(&mutex))){
            fprintf(stderr,"线程2使用阻塞方式获取互斥锁失败:%s\n",strerror(ret_lock));
            //return -1;//针对线程入口函数返回值为void*类型,所以不能使用return -1
            return NULL;
        }
        printf("线程2使用阻塞方式获取互斥锁成功\n");
        #else
        //非阻塞方式加锁
        int ret_lock=0;
        if(0 != (ret_lock = pthread_mutex_trylock(&mutex))){
            fprintf(stderr,"线程2使用非阻塞方式获取互斥锁失败:%s\n",strerror(ret_lock));
            //return -1;//针对线程入口函数返回值为void*类型,所以不能使用return -1
            return NULL;
        }
        printf("线程2使用非阻塞方式获取互斥锁成功\n");
        #endif

        if(num>50){
            num-=50;
            printf("线程2将num-50,num=%d\n",num);
        }
        else{
            printf("线程2发现num<50,不再执行-50\n");
            //解锁
            if(0 != (ret_unlock = pthread_mutex_unlock(&mutex))){
                fprintf(stderr,"1-线程2解锁失败:%s\n",strerror(ret_lock));
                return NULL;
            }
            printf("1-线程2解锁成功\n");
            break;
        }
        //解锁
        if(0 != (ret_unlock = pthread_mutex_unlock(&mutex))){
            fprintf(stderr,"2-线程2解锁失败:%s\n",strerror(ret_lock));
            return NULL;
        }
        printf("2-线程2解锁成功\n");
        sleep(1);//目的:线程1与线程2进行交替执行
    }
    return NULL;
}

int main(int argc,const char *argv[]){
    #if 1
    //动态初始化互斥锁(常用)
    //动态初始化互斥锁会检查错误,若失败则返回错误码
    int ret_mutex_init=0;
    if(0 != (ret_mutex_init = pthread_mutex_init(&mutex,NULL))){
        fprintf(stderr,"动态初始化互斥锁失败:%s\n",strerror(ret_mutex_init));
        return -1;
    }
    printf("动态初始化互斥锁成功\n");
    #else
    //静态初始化互斥锁
    //静态初始化互斥锁不会检查错误,所以不需要使用pthread_mutex_init()函数
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    #endif

    //创建新线程1
    pthread_t tid1;
    int ret_thread1=0;
    if(0 != (ret_thread1 = pthread_create(&tid1,NULL,thread1,NULL))){
        fprintf(stderr,"线程1创建失败:%s\n",strerror(ret_thread1));
        return -1;
    }
    printf("线程1创建成功,tid1=%ld\n",tid1);

    //创建新线程2
    pthread_t tid2;
    int ret_thread2=0;
    if(0 != (ret_thread2 = pthread_create(&tid2,NULL,thread2,NULL))){
        fprintf(stderr,"线程2创建失败:%s\n",strerror(ret_thread2));
        return -1;
    }
    printf("线程2创建成功,tid2=%ld\n",tid2);

    //等待线程1退出
    int ret_join1=0;
    if(0 != (ret_join1 = pthread_join(tid1,NULL))){
        fprintf(stderr,"线程1回收失败:%s\n",strerror(ret_join1));
        return -1;
    }
    printf("线程1回收成功\n");

    //等待线程2退出
    int ret_join2=0;
    if(0 != (ret_join2 = pthread_join(tid2,NULL))){
        fprintf(stderr,"线程2回收失败:%s\n",strerror(ret_join2));
        return -1;
    }
    printf("线程2回收成功\n");

    //销毁互斥锁
    int ret_destroy=0;
    if(0 != (ret_destroy = pthread_mutex_destroy(&mutex))){
        fprintf(stderr,"互斥锁销毁失败:%s\n",strerror(ret_join2));
        return -1;
    }
    printf("互斥锁销毁成功\n");
    return 0;
}

二、条件变量

(一)定义与作用

条件变量是并发编程中用于实现线程间"等待-通知"机制的同步工具,可让线程在某个条件不满足时挂起,待条件满足后被唤醒。

为什么需要条件变量?仅用互斥锁能保证共享资源访问的互斥性(同一时刻只有一个线程使用共享资源 ),但无法解决 "线程需等待特定条件成立才能继续执行 " 的场景(比如生产者 - 消费者模型中,消费者需等待生产者生产出数据才能消费)。

条件变量可结合互斥锁,让线程在条件不满足时主动释放锁并等待 ,条件满足时被唤醒以重新获取锁继续执行,灵活应对依赖条件的同步需求。

核心作用:实现线程间的 "条件等待与通知",使线程能在特定条件下协作,避免线程忙等情况消耗 CPU资源,提升并发效率。

(二)相关函数

pthread_cond_init函数

所需头文件:#include <pthread.h>

原型:int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);动态初始化条件变量(也可以静态初始化条件变量)

参数:

  • cond:用于指定要初始化的条件变量
  • attr:设置条件变量的属性,若为NULL则使用系统默认属性
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 静态初始化条件变量

返回值:成功则返回0;失败则返回非0错误码。

cpp 复制代码
    #if 1
    //动态初始化条件变量(常用)
    //动态初始化条件变量会进行错误检查,失败返回非0错误码
    int ret_cond_init=0;
    if(0 != (ret_cond_init = pthread_cond_int(&cond,NULL))){
        fprintf(stderr,"动态初始化条件变量失败:%s\n",strerror(ret_cond_init));
        return -1;
    }
    printf("动态初始化条件变量成功\n");
    #else
    //静态初始化条件变量
    //静态初始化条件变量不会进行错误检查
    pthread_cond_t cond_static=PTHREAD_COND_INITIALIZER;
    #endif

pthread_cond_signal函数

所需头文件:#include <pthread.h>

原型:int pthread_cond_signal(pthread_cond_t *cond); 唤醒至少一个等待在指定条件变量(cond)上的线程;唤醒的线程不会立刻执行,什么时候加锁成功什么时候执行。

参数cond:指定要操作的条件变量

返回值:成功则返回0;失败则返回非0错误码。

cpp 复制代码
    //3.唤醒消费者
    //唤醒消费者线程后消费者不会立刻执行
    //消费者获得锁(加锁)之后才会执行
    int ret_signal=0;
    if(0 != (ret_signal = pthread_cond_signal(&cond))){
        fprintf(stderr,"生产者线程唤醒消费者线程失败:%s\n",strerror(ret_signal));
        //唤醒消费者失败则需要解锁
        pthread_mutex_unlock(&mutex);
        return NULL;
    }
    printf("生产者线程唤醒消费者线程成功\n");

pthread_cond_broadcast函数

所需头文件:#include <pthread.h>

原型:int pthread_cond_broadcast(pthread_cond_t *cond); 唤醒所有等待在指定条件变量(cond)上的线程;

参数cond:指定要操作的条件变量

返回值:成功则返回0;失败则返回非0错误码。

cpp 复制代码
    //唤醒所有等待在条件变量上的线程---适用多个消费者情况
    int ret_broadcast=0;
    if(0 != (ret_broadcast = pthread_cond_broadcast(&cond))){
        fprintf(stderr,"生产者线程唤醒所有消费者线程失败:%s\n",strerror(ret_signal));
        // //唤醒消费者失败则需要解锁
        // pthread_mutex_unlock(&mutex);
        return NULL;
    }
    printf("生产者线程唤醒所有消费者线程成功\n");

pthread_cond_wait函数

所需头文件:#include <pthread.h>

原型:int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); 线程阻塞在条件变量cond上,等待被唤醒 ;同时,该函数会自动释放传入的互斥锁mutex ,当线程被唤醒时,又会自动重新获取该互斥锁;保证了线程在等待条件过程中互斥锁的正确管道,避免死锁等问题。

参数:

  • cond:用于指定线程等待的条件变量
  • mutex:与条件变量配合使用,保证线程在等待条件时的互斥(同一时刻只有一个线程使用共享资源)。

返回值:成功则返回0;失败则返回非0错误码。

cpp 复制代码
        int ret_wait=0;
        if(0 != (ret_wait = pthread_cond_wait(&cond,&mutex))){
             fprintf(stderr,"消费者线程等待被唤醒失败:%s\n",strerror(ret_wait));
            return NULL;
        }

pthread_cond_destroy函数

所需头文件:#include <pthread.h>

原型:int pthread_cond_destroy(pthread_cond_t *cond); 销毁条件变量相关的资源

参数cond:要销毁的条件变量

返回值:成功则返回0;失败则返回非0错误码。

cpp 复制代码
    //销毁条件变量
    int ret_cond_destroy=0;
    if(0 != (ret_cond_destroy = pthread_cond_destroy(&cond))){
        fprintf(stderr,"销毁条件变量失败:%s\n",strerror(ret_mutex_destroy));
        return -1;
    }
    printf("销毁条件变量成功\n");

(三)完整代码

cpp 复制代码
//使用条件变量
//一个生产者线程、一个消费者线程
//条件:生产者生产完数据,消费者才可以消费数据
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
pthread_cond_t cond;//全局条件变量
pthread_mutex_t mutex;
pthread_t tid1,tid2;
int data;//数据---------生产的数据
int flag;//标志位---------生产是否完成

//生产者线程--需要加锁解锁
void *thread1(void *arg){
    /*
        流程:
        1.加锁()保护共享资源
        2.生产数据(修改共享资源)
        3.唤醒消费者(告知消费者数据已经生产完成)
        4.解锁
    */
    //1.加锁
    int ret_lock=0;
    if(0 != (ret_lock = pthread_mutex_lock(&mutex))){
        fprintf(stderr,"生产者线程加锁失败:%s\n",strerror(ret_lock));
        return NULL;
    }
    printf("生产者线程加锁成功\n");
    //2.生产
    printf("生产者正在生产数据,预计3秒\n");
    sleep(3);
    data=123;
    flag=1;
    printf("生产者线程生产数据完成,数据:%d\n",data);
    //3.唤醒消费者
    #if 1
    //至少有一个正在等待被唤醒的线程---适用于有一个消费者的情况
    //唤醒消费者线程后消费者不会立刻执行
    //消费者获得锁(加锁)之后才会执行
    int ret_signal=0;
    if(0 != (ret_signal = pthread_cond_signal(&cond))){
        fprintf(stderr,"生产者线程唤醒消费者线程失败:%s\n",strerror(ret_signal));
        //唤醒消费者失败则需要解锁
        pthread_mutex_unlock(&mutex);
        return NULL;
    }
    printf("生产者线程唤醒消费者线程成功\n");
    #else
    //唤醒所有等待在条件变量上的线程---适用多个消费者情况
    int ret_broadcast=0;
    if(0 != (ret_broadcast = pthread_cond_broadcast(&cond))){
        fprintf(stderr,"生产者线程唤醒所有消费者线程失败:%s\n",strerror(ret_signal));
        // //唤醒消费者失败则需要解锁
        // pthread_mutex_unlock(&mutex);
        return NULL;
    }
    printf("生产者线程唤醒所有消费者线程成功\n");
    #endif
    //4.解锁
    int ret_unlock=0;
    if(0 != (ret_unlock = pthread_mutex_unlock(&mutex))){
        fprintf(stderr,"生产者线程解锁失败:%s\n",strerror(ret_lock));
        return NULL;
    }
    printf("生产者线程解锁成功\n");
    //生产者线程退出
    pthread_exit(NULL);
    return NULL;
}

//消费者线程--需要加锁解锁
void *thread2(void *arg){
    /*
        流程:
        1.加锁(保护共享资源)
        2.等待被唤醒(pthread_cond_wait会自动解锁,等待生产者生产完数据解锁后自动加锁)
        3.唤醒之后消费数据
        4.解锁
    */
    int ret_lock=0;
    if(0 != (ret_lock = pthread_mutex_lock(&mutex))){
        fprintf(stderr,"消费者线程加锁失败:%s\n",strerror(ret_lock));
        return NULL;
    }
    printf("消费者线程加锁成功\n");
    //2.等待被唤醒(生产者未生产出数据)
    while(!flag){
        int ret_wait=0;
        if(0 != (ret_wait = pthread_cond_wait(&cond,&mutex))){
             fprintf(stderr,"消费者线程等待被唤醒失败:%s\n",strerror(ret_wait));
            return NULL;
        }
    }
    //3.唤醒之后消费数据
    printf("消费者线程等待被唤醒成功,开始消费数据\n");
    printf("消费者开始消费数据,数据:%d\n",data);
    flag=0;//消费者已经消费数据,修改标志位
    //4.解锁
    int ret_unlock=0;
    if(0 != (ret_unlock = pthread_mutex_unlock(&mutex))){
        fprintf(stderr,"生产者线程解锁失败:%s\n",strerror(ret_lock));
        return NULL;
    }
    printf("生产者线程解锁成功\n");
    return NULL;
}

int main(int argc,const char *argv[]){
    #if 1
    //动态初始化条件变量(常用)
    //动态初始化条件变量会进行错误检查,失败返回非0错误码
    int ret_cond_init=0;
    if(0 != (ret_cond_init = pthread_cond_init(&cond,NULL))){
        fprintf(stderr,"动态初始化条件变量失败:%s\n",strerror(ret_cond_init));
        return -1;
    }
    printf("动态初始化条件变量成功\n");
    #else
    //静态初始化条件变量
    //静态初始化条件变量不会进行错误检查
    pthread_cond_t cond_static=PTHREAD_COND_INITIALIZER;
    #endif

    //动态初始化互斥锁
    int ret_mutex_init=0;
    if(0 != (ret_mutex_init = pthread_mutex_init(&mutex,NULL))){
        fprintf(stderr,"动态初始化互斥锁失败:%s\n",strerror(ret_mutex_init));
        return -1;
    }
    printf("动态初始化互斥锁成功\n");

    //创建生产者线程
    int ret_tid1=0;
    if(0 != (ret_tid1 = pthread_create(&tid1,NULL,thread1,NULL))){
        fprintf(stderr,"线程1创建失败:%s\n",strerror(ret_tid1));
        return -1;
    }
    printf("线程1创建成功\n");

    //创建消费者线程
    int ret_tid2=0;
    if(0 != (ret_tid2 = pthread_create(&tid2,NULL,thread2,NULL))){
        fprintf(stderr,"线程2创建失败:%s\n",strerror(ret_tid2));
        return -1;
    }
    printf("线程2创建成功\n");

    //等待线程1与线程2结束,回收资源
    int ret_join=0;
    if(0 != (ret_join = pthread_join(tid1,NULL))){
        fprintf(stderr,"回收线程1资源失败:%s\n",strerror(ret_join));
        return -1;
    }
    printf("回收线程1资源成功\n");
    if(0 != (ret_join = pthread_join(tid2,NULL))){
        fprintf(stderr,"回收线程2资源失败:%s\n",strerror(ret_join));
        return -1;
    }
    printf("回收线程2资源成功\n");

    //销毁互斥锁
    int ret_mutex_destroy=0;
    if(0 != (ret_mutex_destroy = pthread_mutex_destroy(&mutex))){
        fprintf(stderr,"销毁互斥锁失败:%s\n",strerror(ret_mutex_destroy));
        return -1;
    }
    printf("销毁互斥锁成功\n");

    //销毁条件变量
    int ret_cond_destroy=0;
    if(0 != (ret_cond_destroy = pthread_cond_destroy(&cond))){
        fprintf(stderr,"销毁条件变量失败:%s\n",strerror(ret_mutex_destroy));
        return -1;
    }
    printf("销毁条件变量成功\n");
    return 0;
}

三、无名信号量

(一)定义与作用

无名信号量是一种用于线程同步或进程同步的工具,通过维护一个计数值来控制对共享资源的访问,可限制同时访问资源的线程或进程数量。

为什么需要无名信号量? 互斥锁只能保证同一时刻一个线程访问资源,而有些场景需要允许多个线程同时访问; 无名信号量的计数值可灵活设置,能实现 "多线程有限并发访问 " 或 "线程间的计数同步",补充互斥锁在多资源并发场景的不足。

核心作用:无名信号量可实现"同一时刻多个进程/线程访问同一共享资源",通过计数值控制共享资源的并发访问数量,支持多线程在一定限制下同时访问资源,或实现线程间基于计数的同步协作。

(二)相关函数

sem_init函数

所需头文件:#include <semaphore.h>

原型:int sem_init(sem_t *sem, int pshared, unsigned int value); 初始化无名信号量

参数:

  • sem:要初始化的信号量
  • pshared = 0:信号量用于进程内的线程间同步 (多个线程共享进程内存,信号量仅在当前进程内有效 );
    pshared != 0:信号量用于进程间同步(需将信号量放在共享内存中,被多个进程映射后共同访问)
  • value:信号量的初始计数,表示可用资源的初始数量
    value = 1:初始时,有 1 个 "资源" 可用,可用于实现互斥(一次一个线程/进程获取 )
    value = 0:初始时,没有"资源"可用,调用 sem_wait 的线程/进程会阻塞 ,直到 sem_post (释放无名信号量)增加计数

返回值:成功则返回0;失败则返回-1,重置错误码。

sem_wait函数

所需头文件:#include <semaphore.h>

原型:int sem_wait(sem_t *sem); 信号量的值减1,申请资源

如果信号量的当前值大于 0 (有可用资源 ),减 1 后函数直接返回(表示"获取资源成功");

如果信号量的当前值等于 0 (无可用资源 ),sem_wait 会阻塞等待可用资源,直到有其他线程/进程调用 sem_post 函数,使得信号量值大于0后,再执行"减 1"并返回

参数sem:无名信号量

返回值:成功则返回0;失败则返回-1,重置错误码(不会改变信号量的值)。

sem_post函数

所需头文件:#include <semaphore.h>

原型:int sem_post(sem_t *sem); 释放信号量 (将信号量的值+1)

参数sem:用于指定对哪个信号量进行释放操作

返回值:成功则返回0**(将信号量的值+1** );失败则返回-1,重置错误码**(不会改变信号量的值)**。

sem_destroy函数

所需头文件:#include <semaphore.h>

原型:int sem_destroy(sem_t *sem); 销毁无名信号量,释放该信号量相关的资源

参数sem:指定对哪个信号量进行销毁操作

返回值:成功则返回0;失败则返回-1,重置错误码。

(三)完整代码

cpp 复制代码
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <semaphore.h>
sem_t sem1,sem2;//全局无名信号量
pthread_t tid1,tid2;
int count=0;//统计生产者生产数据的次数

//生产者线程
void *thread1(void *arg){
    /*
        流程:
        1.申请资源(申请信号量2)
        2.生产数据(修改共享资源)
        3.释放资源(释放信号量1)(告知消费者生产已完成)
    */
    while(1){
        //1.申请资源
        int ret_wait=0;
        if(-1 == (ret_wait = sem_wait(&sem2))){
            perror("生产者申请信号量2资源失败");
            return NULL;
        }
        printf("生产者申请信号量2资源成功\n");

        //2.生产数据
        printf("生产者正在生产数据,预计3秒\n");
        sleep(3);
        count++;
        printf("生产者生产数据完成,当前资源数量:%d\n",count);
        
        //3.释放资源(释放信号量1,告知消费者生产完成)
        int ret_post=0;
        if(-1 == (ret_post = sem_post(&sem1))){
            perror("生产者释放信号量1资源失败");
            return NULL;
        }
        printf("生产者释放信号量1资源成功\n");
    }
    return NULL;
}

//消费者线程
void *thread2(void *arg){
    /*
        流程:
        1.申请资源(申请信号量1)
        2.消费数据(修改共享资源)
        3.释放资源(释放信号量2)(告知生产者数据已消费)
    */
    while(1){
        //1.申请资源
        int ret_wait=0;
        if(-1 == (ret_wait = sem_wait(&sem1))){
            perror("消费者申请信号量1资源失败");
            return NULL;
        }
        printf("消费者申请信号量1资源成功\n");

        //2.消费数据
        printf("消费者正在消费数据,预计3秒\n");
        sleep(3);
        count--;
        printf("消费者消费数据完成,当前资源数量:%d\n",count);
 
        //3.释放资源(释放信号量2,告知生产者消费完成)
        int ret_post=0;
        if(-1 == (ret_post = sem_post(&sem2))){
            perror("消费者释放信号量2资源失败");
            return NULL;
        }
        printf("消费者释放信号量2资源成功\n");
    }

    return NULL;
}

int main(int argc,const char *argv[]){
    //初始化无名信号量1
    //第二个参数为0:用于进程内线程间同步
    //第三个参数为0:目前没有可用资源
    int ret_init=0;
    if(-1 == (ret_init = sem_init(&sem1,0,0))){
        perror("初始化无名信号量1失败");
        return -1;
    }
    printf("初始化无名信号量1成功\n");
    //初始化无名信号量2---有1个可用资源
    if(-1 == (ret_init = sem_init(&sem2,0,1))){
        perror("初始化无名信号量2失败");
        return -1;
    }
    printf("初始化无名信号量2成功\n");

    //创建线程1
    int ret_tid1=0;
    if(0 != (ret_tid1 = pthread_create(&tid1,NULL,thread1,NULL))){
        fprintf(stderr,"线程1创建失败:%s\n",strerror(ret_tid1));
        return -1;
    }
    printf("线程1创建成功\n");

    //创建线程2
    int ret_tid2=0;
    if(0 != (ret_tid2 = pthread_create(&tid2,NULL,thread2,NULL))){
        fprintf(stderr,"线程2创建失败:%s\n",strerror(ret_tid2));
        return -1;
    }
    printf("线程2创建成功\n");

    //等待线程1与线程2结束,回收资源
    int ret_join=0;
    if(0 != (ret_join = pthread_join(tid1,NULL))){
        fprintf(stderr,"回收线程1资源失败:%s\n",strerror(ret_join));
        return -1;
    }
    printf("回收线程1资源成功\n");
    if(0 != (ret_join = pthread_join(tid2,NULL))){
        fprintf(stderr,"回收线程2资源失败:%s\n",strerror(ret_join));
        return -1;
    }
    printf("回收线程2资源成功\n");

    //销毁无名信号量
    //此程序不会销毁无名信号量---因为前面线程1,2都在无限循环
    int ret_destroy=0;
    if(0 != (ret_destroy = sem_destroy(&sem1))){
        perror("销毁无名信号量1失败");
        return -1;
    }
    printf("销毁无名信号量1成功\n");
    if(0 != (ret_destroy = sem_destroy(&sem2))){
        perror("销毁无名信号量2失败");
        return -1;
    }
    printf("销毁无名信号量2成功\n");
    return 0;
}

四、线程的互斥与同步应用场景

相关推荐
2401_862880821 小时前
input 子系统框架 、platform 驱动框架
linux·驱动开发·嵌入式硬件
十年磨一剑~1 小时前
LVDS 背光控制问题排查与 DTS 修正文档
linux·ubuntu
有毒的教程1 小时前
Linux底层存储路由配置
linux·运维·网络
geats人山人海1 小时前
java 7异常
android·java·开发语言
瑞克-桑切斯1 小时前
2、Shell 条件语句
java·linux·服务器
Pniubi1 小时前
分片的意义?
java·开发语言
阿皮 API2 小时前
Shopee虾皮商品详情API接口调用实战:Python获取东南亚商品数据与JSON解析
开发语言·python·json
梦帮科技2 小时前
Linux 内存取证学与 Rootkit 猎杀——Volatility 核心剖析与内核隐藏模块逆向清除
linux·运维·服务器·神经网络·算法·网络安全
无足鸟ICT2 小时前
【RHCA+】软件包
linux·运维·服务器