多线程--线程安全

Posted 李憨憨_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程--线程安全相关的知识,希望对你有一定的参考价值。

线程安全

概念:描述的是线程中对临界资源的访问操作是安全的
  实现:同步与互斥
    互斥:通过对临界资源同一时间的唯一访问保证访问操作安全
   同步:通过条件判断使对临界资源访问或获取更加合理

互斥的实现

互斥的实现:互斥锁
互斥锁:本质就是一个只有0/1的计数器,用于标记临界资源的访问状态
 0-不可访问;1-可访问
 实现互斥原理:在访问临界资源之前加锁(判断是否可访问),不可访问则阻塞。访问资源完毕之后解锁(将资源状态置为可访问)
互斥锁自身计数的操作是一个原子操作
接口介绍:
 1.定义互斥锁变量
   pthread_mutex_t mutex;
 2.初始化互斥锁变量
  pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
   int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
 3.在访问临界资源之前加锁
  int pthread_mutex_lock(pthread_mutex_t *mutex);
   int pthread_mutex_trylock(pthread_mutex_t *mutex);
 4.在访问临界资源之后解锁
  int pthread_mutex_unlock(pthread_mutex_t *mutex);
 5.销毁互斥锁
  int pthread_mutex_destroy(pthread_mutex_t *mutex);

黄牛抢票(代码演示)

  1 //黄牛抢票
  2 #include<stdio.h>
  3 #include<unistd.h>
  4 #include<stdlib.h>
  5 #include<pthread.h>
  6 int tickets = 100;
  7 void *scalpers(void *arg)
  8 {
  9     while(1){
 10         if(tickets > 0){
 11             usleep(1);
 12             printf("I got a ticket: %d\\n", tickets);
 13             tickets--;
 14         }else{
 15             pthread_exit(NULL);
 16         }
 17     }
 18     return NULL;
 19 }
 20  int main()
 21 {
 22     pthread_t tid[4];
 23     int i = 0;
 24     int ret;
 25     for(i = 0;i < 4;i++){
 26         ret = pthread_create(&tid[i], NULL, scalpers, NULL);
 27         if(ret != 0){
 28             printf("thread create error\\n");
 29             return -1;
 30         }
 31     }
 32     for(i = 0;i < 4;i++){
 33         pthread_join(tid[i], NULL);
 34     }
 35     return 0;
 36 }

在这里插入图片描述


所以要使用互斥锁
在这里插入图片描述
此时就正常了
在这里插入图片描述

死锁

死锁:程序运行流程因为某种原因卡死无法继续推进
  死锁产生的原因:死锁产生的四个必要条件
  1.互斥条件:一个资源同一时间只有一个进程/线程能够访问
   2.不可剥夺条件:我加的锁只有我能解,别人不能解
   3.请求与保持条件:加了A锁后请求B锁,B请求不到,A不释放
   4.环路等待条件:线程1加了A锁,请求B锁;线程2加了B请求A
 预防死锁:破坏死锁产生的必要条件(3,4)
   1.一定保证加/解锁顺序一致;
   2.请求不到第二个锁则释放已有的;
避免死锁:银行家算法....... 已有资源 线程已有资源 线程请求的新资源

同步的实现

同步的实现:通过条件判断实现对资源获取的合理性--条件变量
 条件变量:pcb等待队列+能够使线程阻塞以及唤醒线程阻塞的接口
  当线程对临界资源访问不合理的时候,则调用阻塞接口阻塞进程;
   若其他线程促使当前线程对资源获取合理,则调用唤醒接口;

在这里插入图片描述


  条件变量实现同步这里,有等待队列,但是只提供了阻塞以及唤醒线程的接口,至于什么时候阻塞,什么时候唤醒线程需要程序员自己判断

  条件变量使用中,对条件判断由程序员自己完成,而条件判断的依据是一个临界资源,访问时需要被保护,因此条件变量需要搭配互斥锁一起使用


  接口介绍:
    1.定义条件变量
  pthread_cond_t cond;
  2.初始化条件变量
  pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
  int pthread_cond_init(pthread_cond_t* cond, pthread_condattr_t* attr);
   3.使线程阻塞
  int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex);
   int pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t * mutex, struct timespec *abstime);
   4.唤醒阻塞的线程
   int pthread_cond_signal(pthread_cond_t *cond);-至少一个
   int pthread_cond_broadcast(pthread_cond_t *cond);-唤醒所有
  5.销毁条件变量
  int pthread_cond_destroy(pthread_cond_t *cond);

以上是关于多线程--线程安全的主要内容,如果未能解决你的问题,请参考以下文章

多个请求是多线程吗

多个用户访问同一段代码

线程学习知识点总结

为啥基于锁的程序不能组成正确的线程安全片段?

markdown 线程安全相关片段

多线程带来的风险——线程安全