在Linux多线程编程中,为了保证线程安全和数据一致性,我们需要使用各种同步机制。下面我将详细介绍五种主要的同步机制:互斥锁、读写锁、自旋锁、条件变量和信号量。
互斥锁(Mutual Exclusion Lock)是最基本的同步机制,用于保护临界区,确保同一时间只有一个线程可以访问共享资源。
#include <pthread.h>
// 初始化互斥锁
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
// 销毁互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
// 加锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
// 尝试加锁(非阻塞)
int pthread_mutex_trylock(pthread_mutex_t *mutex);
// 解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
pthread_mutex_t mutex;
void* thread_func(void* arg) {
pthread_mutex_lock(&mutex);
// 临界区代码
pthread_mutex_unlock(&mutex);
return NULL;
}
int main() {
pthread_mutex_init(&mutex, NULL);
pthread_t thread;
pthread_create(&thread, NULL, thread_func, NULL);
// ...
pthread_mutex_destroy(&mutex);
return 0;
}
读写锁允许多个线程同时读取共享资源,但写操作需要独占访问。适用于读多写少的场景。
#include <pthread.h>
// 初始化读写锁
int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
// 销毁读写锁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
// 获取读锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
// 尝试获取读锁
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
// 获取写锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
// 尝试获取写锁
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
// 释放读写锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
pthread_rwlock_t rwlock;
int shared_data = 0;
void* reader(void* arg) {
pthread_rwlock_rdlock(&rwlock);
printf("Reader: %d\n", shared_data);
pthread_rwlock_unlock(&rwlock);
return NULL;
}
void* writer(void* arg) {
pthread_rwlock_wrlock(&rwlock);
shared_data++;
printf("Writer: %d\n", shared_data);
pthread_rwlock_unlock(&rwlock);
return NULL;
}
自旋锁与互斥锁类似,但在获取不到锁时不会使线程睡眠,而是不断循环检查锁状态(自旋)。
#include <pthread.h>
// 初始化自旋锁
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
// 销毁自旋锁
int pthread_spin_destroy(pthread_spinlock_t *lock);
// 加锁
int pthread_spin_lock(pthread_spinlock_t *lock);
// 尝试加锁
int pthread_spin_trylock(pthread_spinlock_t *lock);
// 解锁
int pthread_spin_unlock(pthread_spinlock_t *lock);
pthread_spinlock_t spinlock;
void* thread_func(void* arg) {
pthread_spin_lock(&spinlock);
// 非常短的临界区代码
pthread_spin_unlock(&spinlock);
return NULL;
}
条件变量用于线程间的条件等待,通常与互斥锁配合使用,允许线程在某个条件不满足时挂起等待。
#include <pthread.h>
// 初始化条件变量
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
// 销毁条件变量
int pthread_cond_destroy(pthread_cond_t *cond);
// 等待条件变量
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
// 带超时的等待
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
const struct timespec *abstime);
// 唤醒一个等待线程
int pthread_cond_signal(pthread_cond_t *cond);
// 唤醒所有等待线程
int pthread_cond_broadcast(pthread_cond_t *cond);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int ready = 0;
void* producer(void* arg) {
pthread_mutex_lock(&mutex);
ready = 1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
return NULL;
}
void* consumer(void* arg) {
pthread_mutex_lock(&mutex);
while (!ready) {
pthread_cond_wait(&cond, &mutex);
}
// 处理数据
pthread_mutex_unlock(&mutex);
return NULL;
}
信号量是一种计数器,用于控制对共享资源的访问数量。可以是二值信号量(类似互斥锁)或计数信号量。
#include <semaphore.h>
// 初始化无名信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
// 销毁信号量
int sem_destroy(sem_t *sem);
// 等待信号量(P操作)
int sem_wait(sem_t *sem);
// 非阻塞等待
int sem_trywait(sem_t *sem);
// 带超时的等待
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
// 释放信号量(V操作)
int sem_post(sem_t *sem);
// 获取信号量当前值
int sem_getvalue(sem_t *sem, int *sval);
sem_t sem;
void* thread_func(void* arg) {
sem_wait(&sem);
// 临界区代码
sem_post(&sem);
return NULL;
}
int main() {
sem_init(&sem, 0, 1); // 二值信号量
pthread_t thread;
pthread_create(&thread, NULL, thread_func, NULL);
// ...
sem_destroy(&sem);
return 0;
}
同步机制 | 适用场景 | 特点 | 性能影响 |
---|---|---|---|
互斥锁 | 一般临界区保护 | 简单易用,线程阻塞 | 上下文切换开销 |
读写锁 | 读多写少场景 | 读并发高,写独占 | 比互斥锁复杂 |
自旋锁 | 极短临界区 | 忙等待,不睡眠 | 消耗CPU但无上下文切换 |
条件变量 | 条件等待 | 必须配合互斥锁 | 高效等待 |
信号量 | 资源计数控制 | 灵活,可跨进程 | 比互斥锁稍重 |
正确使用这些同步机制可以有效避免多线程程序中的竞态条件、死锁等问题,保证程序的正确性和性能。