线程同步

线程

许可 进程调度 I/O请求 时间片完 I/O完成 释放 创建 就绪 执行 阻塞 终止
新创建进程首先处于新状态
当OS接纳新状态进程为就绪进程
OS只能从就绪进程中选一个进程执行
执行状态的进程执行完毕,或者被取消,则转化为终止状态
分时系统中,时间片用完,或优先级高的进程到来,将终止优先级低的进程执行
执行进程需要等待某事件发生.通常因进程需要的系统调用不能立即执行而阻塞
当阻塞进程等待的事件I/O完成,就转化为就绪状态
某些系统允许父进程在任何情况下终止其子进程.

背景:由于创建进程(PCB 状态 内存分配 进程ID 等),当创建多个进程会占用大量的内存空间,导致效率降低,创建线程只需要少量的内存资源,同一个进程的线程共享资源,
线程是程序运行的过程,是计算机资源调度的基本单位。

互斥锁

1.pthread_mutex_t mutex;   //声明锁
2.pthread_mutex_init(&mutex); //初始化互斥锁
3.pthread_mutex_lock(&mutex); //进入区
...临界区代码...
4.pthread_mutex_unlock(&mutex);//退出区
5.pthread_mutex_destroy(&mutex);//释放互斥资源

死锁,

一组进程中的每一个进程都在等待仅由该组进程中的其他进程才能引发的事件

原因
1.竞争不可抢占资源引发的死锁
2.竞争可消耗资源引发的死锁
3.进程推进顺序不当引发的死锁

产生死锁的必要条件
 互斥条件
 请求和保持条件
 不可抢占条件
 循环等待条件

解决死锁的方法

预防死锁

避免死锁

检测死锁

接除死锁


读写锁

共享-独占锁.当读写锁以读模式锁住时,它是以共享模式锁住的;但当它以写模式锁住时,它是以独占模式锁住的.写独占.读共享,写优先级高
使用场合
适合对数据结构读的次数大于写的次数的情况
特性

函数
1.定义读写锁
pthread_rwlock_t rwlock;
2.初始化读写锁
int pthread_rwlock_init(
			pthread_rwlock_t *restrict rwlock,
			const pthread_rwlockattr_t *restrict attr);
			第一个参数:rwlock-读写锁
			第二个参数:attr-读写锁属性,传NULL为默认属性
3.销毁读写锁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
4.加读锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
5.尝试加读锁
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
6.加写锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
7.尝试加写锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
8.解锁
int pthread_rwlock_unlock(&pthread_rwlock_t *rwlock);


例:

/*
   *读写锁
   *
   * 3个线程不定时写同一全局资源,5个线程不定时读同一个全局资源
   */
  
  #include<string.h>    //sterror
  #include <pthread.h>
  #include <sys/types.h>
  #include <unistd.h>
  #include<stdlib.h>
  #include<stdio.h>
  
  
  pthread_rwlock_t rwlock;
  
  int number=0;
  void *write(void *arg){
  
  
          int i=*(int *)arg;
          while(1){
  
          //写锁
          pthread_rwlock_wrlock(&rwlock);
          int n=number;
          n++;
          number=n;
          //sleep(rand()%3);
          printf("W[%d]-write:[%d]\n",i,number);
          //解锁
          pthread_rwlock_unlock(&rwlock);
          sleep(rand()%3);
          }
  
  }
  
  void *read(void *arg){
  
          int i=*(int *)arg;
          while(1){
          //读锁  
          pthread_rwlock_rdlock(&rwlock);
          printf("R[%d]-read:[%d]\n",i,number);
          //sleep(rand()%3);
          //解锁
          pthread_rwlock_unlock(&rwlock);
          sleep(rand()%3);
          }
  
  }

  int main()
  {
          pthread_t t[8];
  
  
          pthread_attr_t *attr=NULL;
  
          int arr[8];
  
          pthread_rwlock_init(&rwlock,NULL);
  
  
          //       int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
          //       void *(*start_routine) (void *), void *arg);
          //       第三个参数是函数
          //       第四个是第三个对应函数的参数
  
          //3个写线程
          for(int i=0;i<3;++i){
          //创建线程
          arr[i]=i;
          int ret=pthread_create(&t[i],attr,write,&arr[i]);
            if(ret){
              printf("create error!!!!,[%s]\n",strerror(ret));//返回错误  sterror函数
              return -1;
            }
          }
  
          //5个读进程
          for(int i=3;i<8;++i){
  
          arr[i]=i;
          int ret=pthread_create(&t[i],attr,read,&arr[i]);
            if(ret){
              printf("create error!!!!,[%s]\n",strerror(ret));//返回错误  sterror函数
              return -1;
            }
  
          }
  
          for(int j=0;j<8;++j){
  
            pthread_join(t[j],NULL);
  
          }
          
          //释放互斥锁
          pthread_rwlock_destroy(&rwlock);
  
          sleep(1);
          return 0;
  
  }


条件变量
	条件变量相关函数
	pthread_cond_t cond; //定义一个条件变量
	int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t  *restrict attr);//初始化
	函数参数:cond:条件变量
			attr:条件变量属性,通常传NULL
	函数返回值:成功返回0,失败返回错误号
	int pthread_cond_destroy(pthread_cond_t *cond);//销毁
	int pthread_cond_wait(pthread_cond_t *restrict cond,
							pthread_mutex_t *restrict mutex);
	函数描述:条件不满足,引起线程阻塞并解锁
			条件满足,解除线程阻塞,并加锁
	int pthread_cond_signal(pthread_cond_t *cond);
	函数描述:唤醒至少一个阻塞在该条件变量上的线程
	函数参数:条件变量
	函数返回值:成功返回0,失败返回错误号在这里插入代码片
生产者消费者
  #include<stdlib.h>
  #include<sys/types.h>
  #include<string.h>
  #include<unistd.h>
  #include<stdio.h>
  #include<pthread.h>

  //定义锁
  pthread_mutex_t mutex;
  //定义条件变量
  pthread_cond_t cond;
  
  typedef struct Node{
  
          int data;
          struct Node *next;
  }Node;
  
  Node *head=NULL;
  void *producer(void *arg)
  {
          int val=*(int *)arg;
          Node *pNode=NULL;
  
          while(1){
  
            pNode =(Node *)malloc(sizeof(Node));
            if(!pNode){
  
                    perror("malloc error");
                    exit(-1);
            }
            pNode->data=rand()%1000;
  
            printf("thread[%d],P:[%d]\n",val,pNode->data);
  
            //加锁
            pthread_mutex_lock(&mutex);
            pNode->next=head;
            head=pNode;
            //解锁
            pthread_mutex_unlock(&mutex);
  
            //唤醒,解除堵塞
            //通知消费者线程解除阻塞
            pthread_cond_signal(&cond);
                      
            sleep(rand()%3);
          }
  
  }
void *consumer(void *arg)
  {
  
          int val=*(int *)arg;
          Node *pNode=NULL;
          while(1){
  
            //加锁
            pthread_mutex_lock(&mutex);
  
            if(!head){
                    //条件不满足,需要阻塞等待
                    //若条件不满足,则阻塞等待并解锁
                    //条件满足(pthread_cond_singal函数通知),解除阻塞并加锁
                    pthread_cond_wait(&cond,&mutex);
            }
  
            //边界情况 
            //只有一个生产者生产了一件商品,此时会调用pthread_cond_signal通知消费者(消费者线程),告诉他有商品可购买,当它通知了
            //多个消费者时,则只有一个消费者得到购买资格(获得锁),然后买到商品(head置为空),然后其余已通知的消费者只会有一个获得购买资格(获得锁),此时就会core掉
            if(!head){
                    //解锁
                    pthread_mutex_unlock(&mutex);
                    continue;
            }
            printf("thread[%d],C:[%d]\n",val,head->data);
            pNode=head;
            head=head->next;
            free(pNode);
            pNode=NULL;
  
            //解锁
            pthread_mutex_unlock(&mutex);
  
            sleep(rand()%3);
          }
  }
  int main(){
  
          pthread_t thread1[5];
          pthread_t thread2[5];
  
          //初始化互斥锁
          pthread_mutex_init(&mutex,NULL);
  
          //条件变量初始化
          pthread_cond_init(&cond,NULL);
  
          int arr[5];
          for(int i=0;i<5;++i){
            //创建子线程1
            arr[i]=i;
            int ret=pthread_create(&thread1[i],NULL,producer,&arr[i]);
            if(ret){
                  printf("thread1 create failed\n");
                  strerror(ret);
            }
            
            //创建子线程2
            ret=pthread_create(&thread2[i],NULL,consumer,&arr[i]);
            if(ret){
                  printf("thread2 create failed\n");
                  strerror(ret);
            }
          }
  
          //回收子线程
          for(int i=0;i<5;++i){
          pthread_join(thread1[i],NULL);
          pthread_join(thread2[i],NULL);
          }
  
          //释放互斥锁
          pthread_mutex_destroy(&mutex); 
          //释放条件变量
          pthread_cond_destroy(&cond);
  }


信号量机制
int sem_init(sem_t* sem,int pshared,unsigned int value);
// 初始化一个信号量
// pshared表示是否在进程间共享,0表示只在线程间共享,否则进程间共享
// value为设置的初始值
int sem_destroy(sem_t* sem);
// 销毁一个线程
int sem_wait(sem_t* sem);
// P操作,对信号量-1
int sem_post(sem_t* sem);
// V操作,信号量+1
int sem_getvalue(sem_t* sem, int* valp);
// 返回信号量的值到valp
>>#include<stdlib.h>
  #include<sys/types.h>
>>#include<string.h>
  #include<unistd.h>
>>#include<stdio.h>
  #include<pthread.h>
  #include<semaphore.h>
  
  
  //定义信号量
  sem_t sem_producer;
  sem_t sem_consumer;
  
  
  typedef struct Node{
  
          int data;
          struct Node *next;
  }Node;
  
  Node *head=NULL;
  void *producer(void *arg)
  {
  
          //int val=*(int *)arg;
          Node *pNode=NULL;
  
          while(1){
  
            pNode =(Node *)malloc(sizeof(Node));
            if(!pNode){
  
                    perror("malloc error");
                    exit(-1);
            }
            pNode->data=rand()%1000;
  
            printf("P:[%d]\n",pNode->data);
  
            //p操作
            sem_wait(&sem_producer);
            pNode->next=head;
            head=pNode;
  
            //V操作
            sem_post(&sem_consumer);
  
            sleep(rand()%3);
          }
  
  }
void *consumer(void *arg)
  {
  
          //int val=*(int *)arg;
          Node *pNode=NULL;
          while(1){
  
            //p操作
            sem_wait(&sem_consumer);
  
            printf("C:[%d]\n",head->data);
            pNode=head;
            head=head->next;
            free(pNode);
            pNode=NULL;
  
            //V操作
            sem_post(&sem_producer);
  
            sleep(rand()%3);
          }
  }
int main(){
  
          pthread_t thread1;
          pthread_t thread2;
  
          //初始化信号量
          //第二个参数 0表示线程 1表示进程
          //第三个参数 为第一个参数初始化
          sem_init(&sem_producer,0,5);
          sem_init(&sem_consumer,0,0);
  
            //创建子线程1
            int ret=pthread_create(&thread1,NULL,producer,NULL);
            if(ret){
                  printf("thread1 create failed\n");
                  strerror(ret);
                  return -1;
            }
  
  
            //创建子线程2
            ret=pthread_create(&thread2,NULL,consumer,NULL);
            if(ret){
                  printf("thread2 create failed\n");
                  strerror(ret);
                  return -1;
            }
  
          //回收子线程
          pthread_join(thread1,NULL);
          pthread_join(thread2,NULL);
  
  
          //释放信号量资源
          sem_destroy(&sem_producer);
          sem_destroy(&sem_consumer);
  
  
  
  }


自旋锁

和互斥锁类似,区别在于 互斥锁和自旋锁的阻塞方式,互斥锁通过让线程睡眠实现阻塞,
自旋锁 通过不断循环让线程忙等,适用于占用自旋锁时间短的情况

int pthread_spin_init(__pthread_spinlock_t* __lock, int__pshared);
int pthread_spin_destroy(__pthread_spinlock_t* __lock);
int pthread_spin_trylock(__pthread_spinlock_t* __lock);
int pthread_spin_unlock(__pthread_spinlock_t* __lock);
int pthread_spin_lock(__pthread_spinlock_t* __lock);
上一篇:嵌入式学习DAY28 --- 线程、同步和互斥问题、如何实现同步和互斥?


下一篇:多线程安全-iOS开发要注意咯!