多线程共享进程资源,并发执行。对于同一资源,当某个线程访问时,其它线程就不可以访问,只能等到该线程不再使用时才可以访问。
同步: 临界资源访问的可控时序性(一个线程完另一个线程才可以操作)
互斥: 对临界资源同一时间的唯一访问性(保护临界资源安全)
方式
1. 互斥锁:简单
2. 读写锁:适用于读的次数大于写的次数
3. 信号量:适用于同步
4. 自旋锁:不推荐
5. 条件变量:不推荐
互斥锁
1. 在访问共享资源(读写)前,对互斥锁进行加锁
2. 在访问后释放锁
3. 加锁后,任何试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放,然后此线程再加锁
注意:
1. 访问共享资源要加锁
2. 加锁不一定成功:如果已经加锁,再加锁则失败
3. 加锁成功后一定要释放
4. 如果有异常,需要在finally中释放锁
5. 加锁会影响效率,尽量只对共享资源代码块加锁而不是整个方法
6. 在静态方法内加锁,注意锁定的对象
7. 尽量使用读写锁,实现并发读(在写时,其它线程的读写都阻塞;在读时,其它线程都可以读)
非阻塞:
1. pthread_mutex_trylock:尝试加锁,返回0成功,其它值失败
2. pthread_mutex_timedlock:和pthread_mutex_lock差不多,我就等这么多时间,你一直不释放锁,让我加锁,时间一到就返回错误。
示例:
#include
#include <pthread.h>
#include <unistd.h>
using namespace std;
pthread_mutex_t mutex ; // 声明
void *task(void *arg) {
pthread_mutex_lock(&mutex); //加锁
sleep(5);
int *p = (int *)arg;
pthread_mutex_unlock(&mutex); //解锁
}
int main()
{
pthread_t id;
int a = 1;
pthread_mutex_init(&mutex1,NULL);//初始化互斥锁
pthread_create(&id1,NULL,myid1,(void*)&a);
pthread_join(id1,NULL);
pthread_mutex_destroy(&mutex1); //销毁锁
return 0;
}
读写锁
1. 只能有一个线程写(此时其它线程不允许读写)
2. 可以多个线程读(此时允许其它线程读,不允许写)
3. 读写锁3种状态:读模式下加锁状态、写模式加锁状态、不加锁状态
4. 如果某线程申请了读锁,其它线程可以再申请读锁,但不能申请写锁
5. 如果某线程申请了写锁,其它线程不能申请读锁,也不能申请写锁
6. 读写锁适合于对数据结构的读次数比写次数多得多的情况
非阻塞:
pthread_rwlock_trywrlock:尝试加锁,如果已经被加读锁或写锁,返回错误
步骤:
1. 初始化读写锁
pthread_rwlock_t rwlock;
pthread_rwlock_init(&rwlock, NULL);
2. 读锁
pthread_rwlock_rdlock(&rwlock);
3. 写锁
pthread_rwlock_wrlock(&rwlock);
4. 解锁
pthread_rwlock_unlock(&rwlock);
5. 销毁
pthread_rwlock_destroy(&rwlock);
信号量
1. 一个非负的整数计数器
2. 原子操作
3. 当信号量值大于 0 时,则可以访问资源,否则将阻塞
4. 一次 P 操作使信号量减1,一次 V 操作使信号量加1
5. 访问资源前-1,访问完+1
6. 当信号量为1时相当于互斥锁
非阻塞:
1. sem_trywait(sem_t *sem):尝试对信号量-1,失败立即返回
2.sem_getvalue(sem_t *sem, int *sval):获取信号量的值
步骤:
#include <semaphore.h>
1. 初始化
sem_t sem_p;
sem_init(&sem_p, 0, 1); // 初始化信号量为1
2. 信号量 P 操作(减 1)
sem_wait(&sem_p);
3. 信号量 V 操作(加 1)
sem_post(&sem_p);
4. 销毁信号量
sem_destroy(&sem_p);