Linux--线程安全-(下)

版权声明:允许转载,请注明文章出处 https://blog.csdn.net/Vickers_xiaowei/article/details/86406133

条件变量

在这里插入图片描述
需要一个条件:表示临界区有没有资源
为什么条件变量要和互斥锁搭配使用?
因为等待需要被唤醒,而被唤醒的前提条件就是条件已经满足,并且这个条件本身就是一个临界资源,因此改变条件的操作需要被保护。

条件变量的初始化及销毁:

int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_init(pthread_cond_t * cond,const pthread_condattr_t * attr);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

没有资源就去阻塞等待:

int pthread_cond_wait(pthread_cond_t * cond,pthread_mutex_t * mutex);
//解锁+等待,当被唤醒时候,它自动获得锁

还有限时等待的函数:

int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,const struct timespec *abstime);

pthread_cond_wait函数会对互斥锁做判断,如果调用线程加锁,就解锁,然后陷入等待,整个过程是原子操作,防止消费者先拿到锁,发现条件变量不满足,无法消费,它陷入阻塞等待,这时候生产者得不到锁。

唤醒在条件变量上的线程:

int pthread_cond_broadcast(pthread_cond_t *cond);//广播唤醒
int pthread_cond_signal(pthread_cond_t *cond);//唤醒第一个等待的条件变量的线程

条件变量代码演示:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <errno.h>

pthread_cond_t cond;
pthread_mutex_t mutex;
int basket = 0;//公共条件,需要互斥锁来保证原子性

void* saler(void*arg)
{
    while(1){
        pthread_mutex_lock(mutex);
        if(basket == 0){
            printf("I sell a good\n");
            basket = 1;
            pthread_cond_signal(&cond);
        pthread_mutex_unlock(mutex);
        }
    }
    return NULL;
}

void* customer(void*arg)
{
    while(1){
        pthread_mutex_lock(mutex);
        if(basket == 0){
            //初始状态等待,睡眠
            //pthread_cond_wait函数会对互斥锁做判断,如果调用线程加锁,就解锁,然后陷入等待,整个过程是原子操作
            pthread_cond_wait(&cond,&mutex);
        }
        printf("I bought a gift for my girl friend!!\n");
        basket = 0;
        pthread_mutex_unlock(mutex);
    }
    return NULL;
}

int main()
{
    pthread_t t1,t2;
    int ret;
    pthread_cond_init(&cond,NULL);//条件变量初始化
    pthread_mutex_init(&mutex,NULL);

    ret = pthread_create(&t1,NULL,saler,NULL);
    if(ret != 0){
        perror("pthread_create error");
        exit(-1);
    }

    ret = pthread_create(&t1,NULL,customer,NULL);
    if(ret != 0){
        perror("pthread_create error");
        exit(-1);
    }
    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    pthread_cond_destroy(&cond);
    pthread_mutex_destroy(&mutex);
    return 0;
}

Posix信号量

POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源的目的。 但POSIX可以用于线程间同步。systemV标准posix实现进程间通信
即可以实现同步也可以实现互斥,既可以用于进程间同步与互斥,也可以用于线程间的同步与互斥。

信号量是什么(具有一个等待队列的计数器)
posix线程同步实现
消费者:没有资源则等待
生产者:生产出来则通知队列中的等待者
1、信号量的初始化

int sem_init(sem_t *sem, int pshared, unsigned int value);
  • If pshared has the value 0, then the semaphore is shared between the threads of a process
    If pshared is nonzero, then the semaphore is shared between processes
    sem:信号量变量名
    value:信号量初始计数
    成功:0 失败:-1

2、信号量的操作(等待/通知)

等待:对于消费者,没有资源则等待。等待信号量,会将信号量的值减1。

int sem_wait(sem_t *sem);//阻塞等待
int sem_trywait(sem_t *sem);//没有资源,报错返回
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);//限时等待,超时报错返回

发布信号量:生产者生产出资源,通知消费者。发布信号量,表示资源使⽤完毕,可以归还资源了。将信号量值加1。

int sem_post(sem_t *sem);

3、信号量的释放

int sem_destroy(sem_t *sem);
Link with -pthread.
/*信号量实现线程同步与互斥
 * 同步:
 *      1、信号量的初始化
 *      2、信号量的操作(等待/通知)
 *      3、信号量的释放
 */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <semaphore.h>

sem_t sem;//定义信号量

void*thr_producer(void*arg)
{
    while(1){
        sleep(1);
        printf("I make a hot beef noodles!!\n");
        sem_post(&sem);
    }
    return NULL;
}

void*thr_customer(void*arg)
{
    while(1){
        sem_wait(&sem);
        printf("It is declicious!!\n");
    }
    return NULL;

}

int main()
{
    pthread_t t1,t2;
    int ret;

    sem_init(&sem,0,0);//信号量初始化
    ret = pthread_create(&t1,NULL,thr_producer,NULL);
    if(ret != 0 ){
        perror("pthread_create error");
        exit(-1);
    }

    ret = pthread_create(&t2,NULL,thr_customer,NULL);
    if(ret != 0 ){
        perror("pthread_create error");
        exit(-1);
    }

    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    sem_destroy(&sem);
    
    return 0;
}

posix线程互斥实现

信号量的操作(等待+通知)
sem_wait()信号量减1
sem_post()发布信号量,表示资源使⽤完毕,可以归还资源了。将信号量值加1。

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

sem_t sem;//定义信号量

int ticket = 100;//黄牛抢票,总票数100

void* buy_ticket(void*arg)
{
    int id = (int)arg;
    while(1){
        sem_wait(&sem);
        if(ticket > 0){
            usleep(1000);
            ticket--;
            printf("%d Buy a ticket,the ticket has %d\n",id,ticket);
        }
        sem_post(&sem);
    }
    return NULL;
}
int main()
{
    pthread_t tid[4];
    int ret;
    sem_init(&sem,0,1);//信号量初始化

    int i = 0;
    for(i=0;i<4;i++){
        ret = pthread_create(&tid[i],NULL,buy_ticket,(void*)i);
        if(ret != 0 ){
            perror("pthread_create error");
            exit(-1);
        }
    }
    pthread_join(tid[0],NULL);
    pthread_join(tid[1],NULL);
    pthread_join(tid[2],NULL);
    pthread_join(tid[3],NULL);
    sem_destroy(&sem);

    return 0;
}

信号量与条件变量在保证同步时候的区别:信号量是修改自己内部的资源计数,这个内部的资源计数就是条件,而条件变量修改的是外部的条件,需要我们用户来修改

STL自身不能保证原子性
信号量的操作是一个原子操作。

猜你喜欢

转载自blog.csdn.net/Vickers_xiaowei/article/details/86406133