Linux系统下的线程锁是多线程编程中用于协调多个线程对共享资源访问的同步机制,不同的锁类型在性能、适用场景上存在明显区别,开发者需要根据实际需求选择合适的锁类型。
常见的Linux线程锁类型
1. 互斥锁(pthread_mutex_t)
互斥锁是最基础的线程锁类型,同一时间只允许一个线程持有锁,其他尝试获取锁的线程会被阻塞,直到锁被释放。它适用于大部分需要独占共享资源的场景,比如修改全局变量、操作共享数据结构等。
互斥锁的基本使用流程是初始化锁、获取锁、操作共享资源、释放锁、销毁锁,示例代码如下:
#include <stdio.h>
#include <pthread.h>
// 定义共享资源
int shared_num = 0;
// 定义互斥锁
pthread_mutex_t mutex;
// 线程执行函数
void* thread_func(void* arg) {
// 获取互斥锁
pthread_mutex_lock(&mutex);
// 操作共享资源
shared_num++;
printf("Thread %ld: shared_num = %dn", (long)arg, shared_num);
// 释放互斥锁
pthread_mutex_unlock(&mutex);
return NULL;
}
int main() {
pthread_t threads[2];
// 初始化互斥锁
pthread_mutex_init(&mutex, NULL);
// 创建两个线程
for (long i = 0; i < 2; i++) {
pthread_create(&threads[i], NULL, thread_func, (void*)i);
}
// 等待线程结束
for (int i = 0; i < 2; i++) {
pthread_join(threads[i], NULL);
}
// 销毁互斥锁
pthread_mutex_destroy(&mutex);
return 0;
}
2. 读写锁(pthread_rwlock_t)
读写锁将访问权限分为读模式和写模式,多个线程可以同时持有读锁,但同一时间只能有一个线程持有写锁,且写锁和读锁互斥。它适用于读操作远多于写操作的场景,比如配置信息读取、缓存数据访问等,能提升并发读的性能。
读写锁的使用示例代码如下:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int config_value = 10;
// 定义读写锁
pthread_rwlock_t rwlock;
// 读线程函数
void* read_thread(void* arg) {
pthread_rwlock_rdlock(&rwlock);
printf("Read thread %ld: config_value = %dn", (long)arg, config_value);
pthread_rwlock_unlock(&rwlock);
return NULL;
}
// 写线程函数
void* write_thread(void* arg) {
pthread_rwlock_wrlock(&rwlock);
config_value += 5;
printf("Write thread: update config_value to %dn", config_value);
pthread_rwlock_unlock(&rwlock);
return NULL;
}
int main() {
pthread_t read_threads[3], write_thread_id;
// 初始化读写锁
pthread_rwlock_init(&rwlock, NULL);
// 创建3个读线程
for (long i = 0; i < 3; i++) {
pthread_create(&read_threads[i], NULL, read_thread, (void*)i);
}
// 创建1个写线程
pthread_create(&write_thread_id, NULL, write_thread, NULL);
// 等待所有线程结束
for (int i = 0; i < 3; i++) {
pthread_join(read_threads[i], NULL);
}
pthread_join(write_thread_id, NULL);
// 销毁读写锁
pthread_rwlock_destroy(&rwlock);
return 0;
}
3. 自旋锁(pthread_spinlock_t)
自旋锁和互斥锁类似,同一时间只允许一个线程持有锁,但自旋锁在获取锁失败时不会让线程进入阻塞状态,而是会循环忙等待直到锁被释放。它适用于锁持有时间极短的场景,比如内核态的短操作,因为忙等待会占用CPU资源,如果锁持有时间长会导致CPU浪费。
自旋锁的使用示例代码如下:
#include <stdio.h>
#include <pthread.h>
int counter = 0;
// 定义自旋锁
pthread_spinlock_t spinlock;
void* thread_func(void* arg) {
// 获取自旋锁
pthread_spin_lock(&spinlock);
counter++;
printf("Thread %ld: counter = %dn", (long)arg, counter);
// 释放自旋锁
pthread_spin_unlock(&spinlock);
return NULL;
}
int main() {
pthread_t threads[2];
// 初始化自旋锁
pthread_spin_init(&spinlock, PTHREAD_PROCESS_PRIVATE);
// 创建线程
for (long i = 0; i < 2; i++) {
pthread_create(&threads[i], NULL, thread_func, (void*)i);
}
// 等待线程结束
for (int i = 0; i < 2; i++) {
pthread_join(threads[i], NULL);
}
// 销毁自旋锁
pthread_spin_destroy(&spinlock);
return 0;
}
4. 条件变量(pthread_cond_t)
条件变量本身不是锁,需要配合互斥锁使用,用于让线程等待某个条件满足后再继续执行。它适用于线程间需要基于条件进行同步的场景,比如生产者消费者模型,消费者线程等待生产者线程生产数据后再消费。
条件变量的使用示例代码如下:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
int data = 0;
int has_data = 0;
pthread_mutex_t cond_mutex;
pthread_cond_t cond;
// 生产者线程
void* producer(void* arg) {
pthread_mutex_lock(&cond_mutex);
data = rand() % 100;
has_data = 1;
printf("Producer: produce data %dn", data);
// 通知等待条件变量的线程
pthread_cond_signal(&cond);
pthread_mutex_unlock(&cond_mutex);
return NULL;
}
// 消费者线程
void* consumer(void* arg) {
pthread_mutex_lock(&cond_mutex);
// 等待条件满足
while (has_data == 0) {
pthread_cond_wait(&cond, &cond_mutex);
}
printf("Consumer: consume data %dn", data);
has_data = 0;
pthread_mutex_unlock(&cond_mutex);
return NULL;
}
int main() {
pthread_t prod, cons;
// 初始化互斥锁和条件变量
pthread_mutex_init(&cond_mutex, NULL);
pthread_cond_init(&cond, NULL);
// 创建生产者和消费者线程
pthread_create(&cons, NULL, consumer, NULL);
sleep(1);
pthread_create(&prod, NULL, producer, NULL);
// 等待线程结束
pthread_join(cons, NULL);
pthread_join(prod, NULL);
// 销毁资源
pthread_mutex_destroy(&cond_mutex);
pthread_cond_destroy(&cond);
return 0;
}
不同线程锁的对比
我们可以通过下表直观对比几种线程锁的特性:
| 锁类型 | 核心特性 | 适用场景 |
|---|---|---|
| 互斥锁 | 独占访问,获取失败阻塞 | 大部分需要独占共享资源的场景 |
| 读写锁 | 读共享写独占,读并发高 | 读多写少的共享资源访问场景 |
| 自旋锁 | 获取失败忙等待,无阻塞开销 | 锁持有时间极短的内核或底层操作 |
| 条件变量 | 配合互斥锁等待条件满足 | 线程间基于条件的同步场景 |
使用线程锁的注意事项
- 避免死锁:获取锁的顺序要一致,不要嵌套获取多个锁时出现交叉获取的情况。
- 锁的粒度要合适:锁的范围太大会影响并发性能,太小可能导致锁操作的开销占比过高。
- 自旋锁不要用在用户态长时间持锁的场景,避免浪费CPU资源。
- 条件变量等待时要使用循环判断条件,避免虚假唤醒导致逻辑错误。
Linux线程锁pthread_mutex_tpthread_rwlock_tpthread_spinlock_tpthread_cond_t修改时间:2026-06-22 21:04:16