线程同步
在多线程环境中,线程之间由于竞争共享资源(临界资源)容易引起数据不一致的问题。一般采用互斥锁(互斥信号量)解决,保证只有一个线程进入临界区。
互斥锁
使用步骤:
- 初始化互斥锁
- 静态创建
/** * 使用宏定义以及初始化锁 */ pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
- 动态创建
/** * 参数: * 1:互斥锁 * 2:互斥锁属性 */ int pthread_mutex_init(pthread_mutex_t * __restrict, const pthread_mutexattr_t * _Nullable __restrict);
- 加锁以及解锁
- 加锁(阻塞)
/** * 参数: * 互斥锁变量 * 返回值: * 是否成功 */ int pthread_mutex_lock(pthread_mutex_t *);
- 尝试加锁(非阻塞)
/** * 参数: * 互斥锁变量 * 返回值: * 成功返回0 * 失败返回错误码 */ int pthread_mutex_trylock(pthread_mutex_t *);
- 解锁
/** * 参数: * 互斥锁变量 * 返回值: * 成功返回0 * 失败返回错误码 */ int pthread_mutex_lock(pthread_mutex_t *);
- 销毁锁
/** * 参数: * 互斥锁变量 * 返回值: * 成功返回0 * 失败返回错误码 */ int pthread_mutex_destroy(pthread_mutex_t *);
读写锁
为了解决性能问题,引入读写锁,特点是:读共享,写独享,写优先级高,读写之间也是互斥的。适合读线程较多的场景。
🌴🌴 注意:在A线程加锁状态下,当B线程请求读锁,然后C线程请求写锁时,在A释放锁的情况下,C请求的写锁先获取到。
读写锁使用步骤与互斥锁基本相同,函数原型如下。
//初始化
pthread_rwlock_t mutex = PTHREAD_RWLOCK_INITIALIZER;
//或者
int pthread_rwlock_init(pthread_rwlock_t * __restrict,
const pthread_rwlockattr_t * _Nullable __restrict);
//加锁(读写)
int pthread_rwlock_rdlock(pthread_rwlock_t *);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *)
int pthread_rwlock_wrlock(pthread_rwlock_t *);
int pthread_rwlock_trywrlock(pthread_rwlock_t *);
//解锁
pthread_rwlock_unlock();
//释放锁
int pthread_rwlock_destroy(pthread_rwlock_t * )
条件变量
条件变量本质不是锁,通常与互斥锁配合使用。
当条件不满足时,阻塞线程。
当条件满足时,通知线程解除阻塞。
区别于Java:
1. Object中wait()、notify()、notifyAll() 是进程间通信(同步)的方式,且必须在synchronized中使用。
2. Java并发包下condition.await()、condition.signal() 也是用于线程通信的。
使用方式:
- 初始化条件变量
pthread_cond_t cond=PTHREAD_COND_INITIALIZER; //或者 int pthread_cond_init( pthread_cond_t * __restrict, const pthread_condattr_t * _Nullable __restrict)
- 条件不满足阻塞
/** * 阻塞并且释放锁 * 当接收到信号加锁继续执行 * * 参数: * 1:条件变量 * 2:锁 * 返回值: * 是否成功 */ int pthread_cond_wait(pthread_cond_t * __restrict, pthread_mutex_t * __restrict)
- 条件满足唤醒
/** * 唤醒阻塞的线程 * 参数: * 条件变量 * 返回值: * 是否成功 */ int pthread_cond_signal(pthread_cond_t *)
- 释放条件变量
int pthread_cond_destroy(pthread_cond_t *);
信号量
信号量是用来标识可同时使用的共享资源的个数。
使用方式:
- 初始化
/** * 参数: * 1:sem_t变量 * 2:0线程同步,1进程同步 * 3:资源数 * 返回值: * 是否成功 */ int sem_init(sem_t *, int, unsigned int)
- 条件判断
当条件不满足时,阻塞线程,信号量自减。
- 阻塞
int sem_wait(sem_t *)
- 非阻塞
int sem_trywait(sem_t *)
- 唤醒
当条件满足时,唤起所有阻塞的线程,信号量自增,同时加锁。
所有阻塞线程同时抢占CPU,抢到CPU时间片的执行。
❗❗ 此时,仍需要条件判断。int sem_post(sem_t *)
- 释放信号量
int sem_destroy(sem_t *)