线程的同步与互斥
多线程执行共享变量的这段代码可能会导致竞争状态,因此我们将此段代码称为临界资源,它是执行共享资源的代码片段,一定不能给多线程同时执行。
互斥并不只是针对多线程的竞争条件,同时还可用于多进程,避免共享资源混乱。
互斥
多个线程在访问(读写)临界资源时存在资源竞争,可以让多个线程访问临界资源时,在同一时刻(排他性)只允许一个线程访问。
使用互斥锁:避免多线程资源竞争,保护临界资源。
1、创建一个互斥锁对象 pthread_mutex_t
2、初始化互斥锁 pthread_mutex_init
3、加锁 pthread_mutex_lock 阻塞等待锁资源
4、解锁 pthread_mutex_unlock
5、销毁锁 pthread_mutex_destroy1、定义:pthread_mutex_t mutex;2、初始化锁int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);功能:将已经定义好的互斥锁初始化。参数:mutex 要初始化的互斥锁atrr 初始化的值,一般是NULL表示默认锁返回值:成功 0失败 非零3、加锁:int pthread_mutex_lock(pthread_mutex_t *mutex);功能:用指定的互斥锁开始加锁代码加锁后的代码到解锁部分的代码属于原子操作,在加锁期间其他进程/线程都不能操作该部分代码如果该函数在执行的时候,mutex已经被其他部分使用则代码阻塞。参数: mutex 用来给代码加锁的互斥锁返回值:成功 0失败 非零4、解锁int pthread_mutex_unlock(pthread_mutex_t *mutex);功能:将指定的互斥锁解锁。解锁之后代码不再排他访问,一般加锁解锁同时出现。参数:用来解锁的互斥锁返回值:成功 0失败 非零5、销毁int pthread_mutex_destroy(pthread_mutex_t *mutex);功能:使用互斥锁完毕后需要销毁互斥锁参数:mutex 要销毁的互斥锁返回值:成功 0失败 非零
同步
异步:多个任务同时工作时,之间没有干扰
线程间的同步:让多个任务在执行某部分程序时,按照先后顺序执行,以同步的方式访问临界资源,具备互斥效果。
同步是一种更为复杂的互斥,而互斥是一种特殊的同步。
也就是说互斥是两个线程之间不可以同时运行,他们会相互排斥,必须等待一个线程运行完毕,另一个才能运行,而同步也是不能同时运行,但他是必须要安照某种次序来运行相应的线程(也是一种互斥)。互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。 同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。
同步实现:信号量
linux下的线程同步 ----> 信号量机制 ----> semaphore.h posix sem_open();信号量的分类:1、无名信号量 ----> 线程间通信2、有名信号量 ----> 进程间通信semaphore
1、信号量的定义 :sem_t sem;信号量的类型 信号量的变量
同步的实现
1、创建一个信号量对象 pthread_mutex_t
2、初始化信号量 pthread_mutex_init
3、申请信号量(P操作) pthread_mutex_lock
4、释放信号量(V操作) pthread_mutex_unlock
5、销毁锁 pthread_mutex_destroy信号量的初始化:int sem_init(sem_t *sem, int pshared, unsigned int value);功能:将已经定义好的信号量赋值。参数:sem 要初始化的信号量pshared = 0 ;表示线程间使用信号量!=0 ;表示进程间使用信号量value 信号量的初始值,一般无名信号量都是二值信号量,0 1 (同步用二值信号量, 计数信号量)0 表示红灯,进程暂停阻塞1 表示绿灯,进程可以通过执行返回值:成功 0失败 -1;
信号量的PV 操作P ===》申请资源===》申请一个信号量 V ===》释放资源===》释放一个信号量P操作对应函数 ==》sem_wait();V操作对应函数 ==》sem_post();int sem_wait(sem_t *sem);
功能:判断当前sem信号量是否有资源可用。如果sem有资源(==1),则申请该资源,程序继续运行如果sem没有资源(==0),则线程阻塞等待,一旦有资源则自动申请资源并继续运行程序。注意:sem 申请资源后会自动执行 sem = sem - 1;
参数:sem 要判断的信号量资源
返回值:成功 0 失败 -1int sem_post(sem_t *sem);
功能:函数可以将指定的sem信号量资源释放并默认执行,sem = sem+1;线程在该函数上不会阻塞。
参数:sem 要释放资源的信号量
返回值:成功 0失败 -1;信号量的销毁int sem_destroy(sem_t *sem);功能:使用完毕将指定的信号量销毁参数:sem要销毁的信号量返回值:成功 0失败 -1;
死锁
死锁: 死锁指的是在多线程环境中,每个执行流(线程)都有未释放的资源,且互相请求对方未释放资源,从而导致陷入永久等待状态的情况。多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。
现象1:忘记释放锁现象2:重复加锁现象3:多线程多锁,抢占锁资源不当如:线程A获取了1锁,线程B获取了2锁,同时线程A还想获取2锁,线程B还想获取1锁产生死锁的四个必要条件:(1) 互斥条件:一个资源每次只能被一个进程使用(一个执行流获取锁后,其它执行流不能再获取该锁)。(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放(执行流本身使用着一把锁并不释放,还在请求别的锁)。(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺(A执行流拿着锁,其它执行流不能释放)。(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系(多个执行流拿着对方想要的锁,并且各执行流还去请求对方的锁)。解决方法:1.锁一定要成对出现2.使线程的加锁顺序一致3.破坏环路等待条件使用非阻塞锁,一旦线程发现请求的锁被使用,就去释放自己拥有的锁