多线程(二)线程安全

线程安全

在多个执行流中对同一个临界资源进行操作访问,而不会造成数据二义,这就是线程安全。
如何实现线程安全: 同步于互斥
互斥:通过保证同一时间只有一个执行流可以对临界资源进行访问(一个执行流访问期间,其它执行流不能访问),来保证对临界资源访问的安全性。
同步:通过一些条件判断来实现多个执行流对临界资源访问的合理性(有资源则访问,没有资源则等待,等有了资源再被唤醒)

如何实现互斥: 互斥锁
如何实现同步: 条件变量 posix 标准的信号量。

互斥锁:

互斥锁—用于标记当前临界资源的访问状态。
计数器: 0/1 0-不可访问 1-可以访问 计数器初值为1.
将需要等待的pcb状态置为可中断休眠状态,等到解锁后,将这些pcb状态再置位运行态。

每一个线程访问临界资源之前,先判断计数,当前临界资源的状态( 是否有人正在访问---- 正在访问的线程将状态置位了0 --不可访问)
1.第一个线程访问的时候,判断可以访问,因此将状态置为不可访问,然后去访问资源。
2.其它线程访问的时候,发现不可访问,就陷入等待。
3.第一个线程访问临界资源完毕后,将状态置位1-可以访问,唤醒等待的线程,大家重新开始竞争这个资源

等待: 将pcb状态置为可中断休眠(则不被操作系统调度)
**唤醒:**将pcb状态置为运行态(则可以开始调度);

pthread_mutex_t 互斥锁变量类型
pthread_mutex_init(pthread_mutex_t * mutex,pthread_mutexattr_t*attr); 初始化互斥锁
pthread_mutex_lock(pthread_mutex_t *mutex); 在临界资源访问之前加锁
pthread_mutex_unlock(pthread_mutex_t *mutex); 在临界资源访问完毕后解锁
pthread_mutex_destroy(pthread_mutex_t * mutex); 销毁互斥锁

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

int tickets=100;
pthread_mutex_t  mutex;
void *thr_tout(void * arg){
        while(1){
         //pthread_mutex_lock  阻塞加锁
         //pthread_mutex_trylock  非阻塞加锁
         //pthread_mutex_timedlock   限时等待的阻塞加锁
         //加锁一定是在访问临界资源之前加锁
                pthread_mutex_lock(&mutex);
                if(tickets>0){
                        usleep(20000);
                        tickets--;
                        //解锁一定是访问临界资源完毕自后
                pthread_mutex_unlock(&mutex);
                }
                else{
                //加锁之后在任意有可能退出线程的地方都要解锁
                pthread_mutex_unlock(&mutex);
                        printf("tout:%p exit\n",pthread_self());
                        pthread_exit(NULL);
                }
        }
        return NULL;
}
int main(){
        int i;
        int ret;
        //互斥锁的初始化
        pthread_mutex_init(&mutex,NULL);
        pthread_t tid[4];
        for(i=0;i<4;i++){
                ret=pthread_create(&tid[i],NULL,thr_tout,NULL);
                if(ret!=0){
                        printf("thread  create error\n");
                        return  -1;
                }
        }
        for(i=0;i<4;i++){
                pthread_join(tid[i],NULL);
        }
        pthread_mutex_destroy(&mutex);
        return 0;

}
                                                            
同步的实现

保证临界资源访问的合理性,有资源的时候可以获取,没有资源的时候则需要线程等待,等待被唤醒(其他线程产生了一个资源的时候);
条件变量: 向用户提供了两个接口:使一个线程等待的接口和唤醒一个线程的接口+等待队列
条件变量只是提供了等待与唤醒的功能,但是什么时候等待,什么时候唤醒,需要用户自己来判断

  1. 判断当前是否有没有资源,若没有资源则阻塞执行流。
  2. 生产了一个资源,唤醒还在等待的执行流

条件变量可以通过提供等待队列和唤醒接口来实现线程间的同步,
但是需要注意的是: 进行条件判断,什么时候该等待中的这个条件判断,需要自己完成。
用户自己判断什么时候该等待,则调用等待接口;另一个执行流促使这个访问条件瞒足之后,需要用户调用唤醒接口来唤醒等待的执行流。

操作接口:
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
pthread_cond_init(pthread_cond_t * ,pthread_condattr_t *attr);
int pthread_cond_wait(pthread_cond_t *, pthread_mutex_t * )
使当前调用执行流陷入阻塞等待
里面执行的操作是解锁 -> 休眠 -》 被唤醒后重新加锁 其中解锁和休眠是原子操作
int pthread_cond_signal(pthread_cond_t * );
唤醒至少一个条件变量等待队列中的执行流。
int pthread_cond_broadcast(pthread_cond_t *)
广播唤醒所有等待的执行流。
int pthread_cond_destroy(pthread_cond_t *);
销毁一个条件变量。

下面来一个实例

//这个demo体会条件变量的基本使用以及操作基本接口
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>

pthread_mutex_t mutex;
pthread_cond_t cond_gourmet;
pthread_cond_t cond_cheif;
int have_food=0;   //0表示没有美食
void * gourmet(void * arg){

        while(1){
                pthread_mutex_lock(&mutex);
                //这里应该用的是循环
                //先加锁。
                        while(have_food==0){
                        //没有美食,需要等待。
                        //并且这个解锁操作和陷入休眠的操作必须是原子操作
                        //解锁 -》 休眠-  -》 被唤醒后重新加锁 
                        pthread_cond_wait(&cond_gourmet,&mutex);
                }
                printf("really delicious\n");
                have_food--;
                pthread_mutex_unlock(&mutex);
                //唤醒厨师              
                pthread_cond_signal(&cond_cheif);
        }
        return NULL;
}
void * cheif(void*arg){
        while(1){

                pthread_mutex_lock(&mutex);
                while(have_food==1){
                        //若做好了美食,但是没人吃也陷入等待。
                        pthread_cond_wait(&cond_cheif,&mutex);
                }
                printf("i make a food\n");
                have_food++;
                //做出美食之后,应该唤醒等待的人,
                pthread_mutex_unlock(&mutex);
                pthread_cond_signal(&cond_gourmet);
                //唤醒一个执行流
        } return NULL;
}

int main(){

        pthread_cond_init(&cond_gourmet,NULL);
        pthread_cond_init(&cond_cheif,NULL);
        pthread_mutex_init(&mutex,NULL);
        pthread_t gourmet_tid,cheif_tid;
        int ret;
        int i;
        for(i=0;i<4;i++){

        ret=pthread_create(&gourmet_tid,NULL,gourmet,NULL);
        if(ret!=0){
                printf("pthread create error!\n");
                return -1;
                }
        }
        for(i=0;i<4;i++){
        ret=pthread_create(&cheif_tid,NULL,cheif,NULL);

        if(ret!=0){
                printf("pthread create error!\n");
                return -1;

                }
        }
        pthread_join(gourmet_tid,NULL);
        pthread_join(cheif_tid,NULL);
        pthread_cond_destroy(&cond_cheif);
        pthread_cond_destroy(&cond_gourmet);  //销毁条件变量。
        pthread_mutex_destroy(&mutex);  //销毁互斥锁。
        return 0;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值