Linux:线程安全

  1. 什么是线程安全?
    当我们多线程对临界资源,或者全局变量静态变量,(多线程执行流共享的资源)进行操作时,容易造成的问题。因为在cpu足够的情况下,多个线程的运行也可能是并行的,因此对临界资源的访问,就会造成争抢操作,然而这种争抢操作,会造成数据的二义性问题,因此线程安全,就是讨论如何保证线程对临界资源的安全访问。

  2. 解决线程安全问题的方法
    同步与互斥操作,同步就是对临界资源访问的时序可控性,互斥就是对临界资源的同一时间的唯一访问性,那么我们如何实现同步与互斥操作呢,在Linux环境下提供了两种不一样的方法:互斥锁和条件变量

  3. 互斥锁的原理和实现
    互斥锁在同一时间内只允许唯一的线程来对临界资源进行操作,互斥锁其实就是一个只有0/1的计数器,1表示有资源能操作,0表示没有资源则阻塞,加锁就是,计数器的1置为0,继续操作,计数器为0则返回阻塞,解锁就是计数器由0置为1,我们通过互斥锁对临界资源进行操作,来保证临界资源的安全访问,因为互斥锁也是一个临界资源所以对互斥锁的操作是原子操作
    互斥锁的函数原型:

      int pthread_mutex_lock(pthread_mutex_t *mutex);
      //阻塞加锁
      int pthread_mutex_trylock(pthread_mutex_t *mutex);
      //非阻塞加锁
      int pthread_mutex_unlock(pthread_mutex_t *mutex);
   	  //解锁

模拟实现抢票系统:


#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
volatile int ticket=100;//防止编译器过度优化
pthread_mutex_t mutex;
void *thr_scalper(void*arg)
{
    int id=(int)arg;
    while(1){
        //在访问资源前加锁
        //      int pthread_mutex_lock(pthread_mutex_t *mutex);
        //      int pthread_mutex_trylock(pthread_mutex_t *mutex);
        //      int pthread_mutex_unlock(pthread_mutex_t *mutex);
        //lock-----阻塞加锁
        //trylock————非阻塞加锁
        pthread_mutex_lock(&mutex); //加锁防止多个线程同时访问临界资源造成资源泄漏,抢票抢到负数票的问题
        if(ticket>0){
            usleep(100);
            printf("I %d--%p get one ticket:%d\n",id,pthread_self(),ticket);
            ticket--;
        }else{
            pthread_mutex_unlock(&mutex);
            pthread_exit(NULL);
        }
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}
int main()
{
    pthread_t tid[4];
    int i=0;
    int ret;
    //初始化锁
    //      int pthread_mutex_destroy(pthread_mutex_t *mutex);
    //      int pthread_mutex_init(pthread_mutex_t *restrict mutex,
    //      const pthread_mutexattr_t *restrict attr);
    //      pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    //mutex:互斥锁变量
    //attr:互斥锁属性,通常置为NULL
    //成功返回0,错误返回错误编号errno
    pthread_mutex_init(&mutex,NULL);
    for(i=0;i<4;i++){
        ret=pthread_create(&tid[i],NULL,thr_scalper,(void*)i);
        if(ret!=0){
            printf("creat thread error\n");
            return -1;
        }
    }
    for(i=0;i<4;i++){
        pthread_join(tid[i],NULL);
    }
    //销毁锁
    pthread_mutex_destroy(&mutex);
    return 0;
}
     

在这里插入图片描述

  1. 同步——条件变量的原理和实现
    同步:在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题,同当一个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了。例如一个线程访问队列时,发现队列为空,它只能等待,只到其它线程将一个节点添加到队列中。这种情况就需要用到条件变量。它的功能是等待+唤醒指定队列。
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);//条件变量初始化
//cond:条件变量的名称
//attr:条件变量的属性,默认为NULL
pthread_cond_destory(&cond);销毁
pthread_cond_wait(&cond,&mutex);死等     
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
             pthread_mutex_t *restrict mutex,
             const struct timespec *restrict abstime);
//abstime等待时间        
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);

用条件变量模拟吃面的程序:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
int have_noodle=0;
pthread_cond_t cond;
pthread_mutex_t mutex;
void*sale_noodle(void*arg)
{
    while(1){
        pthread_mutex_lock(&mutex);
        if(have_noodle==0){
            //sleep(1);
            printf("creat noodle!\n");
            have_noodle=1;
        //pthread_mutex_unlock(&mutex);    
            pthread_cond_broadcast(&cond);//signal单个唤醒,broadcast多个唤醒
        }
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}
void*buy_noodle(void*arg)
{
    while(1){
        pthread_mutex_lock(&mutex);
        while(have_noodle==0){
           // pthread_mutex_unlock(&mutex);//死等之前要解锁,而且解锁和死等必须是原子操作
            pthread_cond_wait(&cond,&mutex);
            //被唤醒后,需要加锁,但是这个锁不是阻塞的,意味着不管是否能加锁都会操作,访问临界资源
            //如果被唤醒的是多个线程,则会出问题,因此需要循环的条件判断
        }
       // pthread_mutex_lock(&mutex);//被唤醒后需要加锁
        printf("i eat it\n");
		 have_noodle=0;
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}
int main()
{
    pthread_t tid1,tid2;
    int ret;
    pthread_mutex_init(&mutex,NULL);
    pthread_cond_init(&cond,NULL);
    //int pthread_cond_init(pthread_cond_t *restrict cond,
    //              const pthread_condattr_t *restrict attr);
    //      cond:条件变量
    //      attr:条件变量属性
    //      成功:0
    //      失败:erron
    //      int pthread_cond_destroy(pthread_cond_t *cond);

    ret=pthread_create(&tid1,NULL,sale_noodle,NULL);
    if(ret!=0){
        printf("creat sale thread error");
        return -1;
    }
    int i=0;
    for(i=0;i<3;i++){
        ret=pthread_create(&tid2,NULL,buy_noodle,NULL);
        if(ret!=0){
            printf("creat sale thread error");
            return -1;
       }
    }
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
	pthread_cond_destroy(&cond);
    pthread_mutex_destroy(&mutex);
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值