读写锁(Read-Write Lock) 是一种特殊的同步原语,专为读多写少场景设计。与互斥锁不同,读写锁允许多个读线程同时持有锁,但写线程必须独占访问。
核心思想:
读写锁状态:
| 状态 | 描述 | 允许的操作 |
|---|---|---|
| 未锁定 | 无任何线程持有锁 | 读锁、写锁 |
| 读锁定 | 一个或多个线程持有读锁 | 读锁(继续加锁)、写锁(阻塞) |
| 写锁定 | 一个线程持有写锁 | 无(所有操作阻塞) |
读写锁存在读优先和写优先两种实现策略,不同的策略会导致不同的性能特性:
读优先(Read-Preference):
写优先(Write-Preference):
Linux实现(NPTL):
pthread_rwlockattr_setkind_np()设置写优先自旋锁(Spinlock) 是一种忙等待(Busy-Waiting)锁机制。当线程无法获取锁时,它不会睡眠或让出CPU,而是在一个循环中不断尝试获取锁。
核心特点:
自旋锁的工作流程:
线程尝试获取锁
↓
锁可用? ──是──→ 获取锁,继续执行
│
否
│
└──→ 在循环中不断重试(自旋)
↓
锁被释放? ──是──→ 获取锁,继续执行
│
否
└──→ 继续自旋
| 特性 | 自旋锁 | 互斥锁 |
|---|---|---|
| 等待方式 | 忙等待(自旋) | 睡眠等待 |
| CPU占用 | 持续占用CPU | 不占用CPU |
| 上下文切换 | 无 | 有 |
| 适用场景 | 锁持有时间极短 | 锁持有时间较长 |
| 多CPU/多核 | 适合 | 适合 |
| 单CPU | 不适合(浪费CPU) | 适合 |
| 中断上下文 | 可以使用 | 不能使用(不能睡眠) |
选择原则:
在嵌入式系统中,读写锁和自旋锁有特定的应用场景:
自旋锁的典型应用:
读写锁的典型应用:
#include <pthread.h>
// 初始化读写锁
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
const pthread_rwlockattr_t *restrict 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);
参数说明:
rwlock:指向读写锁的指针attr:读写锁属性,NULL使用默认属性返回值:
#include <pthread.h>
// 初始化读写锁属性
int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);
// 销毁读写锁属性
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr);
// 获取/设置进程共享属性
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *restrict attr,
int *restrict pshared);
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared);
// 获取/设置写优先属性(非标准扩展)
int pthread_rwlockattr_getkind_np(const pthread_rwlockattr_t *attr,
int *pref);
int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, int pref);
写优先设置:
// 设置为写优先
pthread_rwlockattr_t attr;
pthread_rwlockattr_init(&attr);
pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
#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);
参数说明:
lock:指向自旋锁的指针pshared:进程共享属性
PTHREAD_PROCESS_PRIVATE:仅在进程内使用(默认)PTHREAD_PROCESS_SHARED:可在进程间共享返回值:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#define NUM_READERS 5
#define NUM_WRITERS 2
#define NUM_OPS 10
// 共享资源
int shared_data = 0;
pthread_rwlock_t rwlock;
// 读线程函数
void *reader(void *arg) {
int id = *(int *)arg;
for (int i = 0; i < NUM_OPS; i++) {
// 获取读锁
pthread_rwlock_rdlock(&rwlock);
// 读取共享数据
printf("[Reader %d] Read: %d\n", id, shared_data);
// 模拟读操作耗时
usleep(100);
// 释放读锁
pthread_rwlock_unlock(&rwlock);
usleep(50);
}
return NULL;
}
// 写线程函数
void *writer(void *arg) {
int id = *(int *)arg;
for (int i = 0; i < NUM_OPS; i++) {
// 获取写锁
pthread_rwlock_wrlock(&rwlock);
// 修改共享数据
shared_data++;
printf("[Writer %d] Write: %d\n", id, shared_data);
// 模拟写操作耗时
usleep(200);
// 释放写锁
pthread_rwlock_unlock(&rwlock);
usleep(100);
}
return NULL;
}
int main() {
pthread_t readers[NUM_READERS], writers[NUM_WRITERS];
int reader_ids[NUM_READERS], writer_ids[NUM_WRITERS];
// 初始化读写锁
pthread_rwlock_init(&rwlock, NULL);
// 创建读线程
for (int i = 0; i < NUM_READERS; i++) {
reader_ids[i] = i;
pthread_create(&readers[i], NULL, reader, &reader_ids[i]);
}
// 创建写线程
for (int i = 0; i < NUM_WRITERS; i++) {
writer_ids[i] = i;
pthread_create(&writers[i], NULL, writer, &writer_ids[i]);
}
// 等待所有线程完成
for (int i = 0; i < NUM_READERS; i++) {
pthread_join(readers[i], NULL);
}
for (int i = 0; i < NUM_WRITERS; i++) {
pthread_join(writers[i], NULL);
}
// 销毁读写锁
pthread_rwlock_destroy(&rwlock);
printf("Final shared_data: %d\n", shared_data);
return 0;
}
编译运行:
gcc -pthread -o rwlock_basic rwlock_basic.c
./rwlock_basic
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#define NUM_THREADS 4
#define NUM_OPS 100
// 共享资源
int shared_counter = 0;
pthread_spinlock_t spinlock;
// 线程函数
void *thread_func(void *arg) {
int id = *(int *)arg;
for (int i = 0; i < NUM_OPS; i++) {
// 获取自旋锁
pthread_spin_lock(&spinlock);
// 临界区操作
shared_counter++;
// 释放自旋锁
pthread_spin_unlock(&spinlock);
}
printf("[Thread %d] Finished, counter = %d\n", id, shared_counter);
return NULL;
}
int main() {
pthread_t threads[NUM_THREADS];
int thread_ids[NUM_THREADS];
// 初始化自旋锁
pthread_spin_init(&spinlock, PTHREAD_PROCESS_PRIVATE);
// 创建线程
for (int i = 0; i < NUM_THREADS; i++) {
thread_ids[i] = i;
pthread_create(&threads[i], NULL, thread_func, &thread_ids[i]);
}
// 等待所有线程完成
for (int i = 0; i < NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
}
// 销毁自旋锁
pthread_spin_destroy(&spinlock);
printf("Final counter: %d (expected: %d)\n", shared_counter, NUM_THREADS * NUM_OPS);
return 0;
}
编译运行:
gcc -pthread -o spinlock_basic spinlock_basic.c
./spinlock_basic
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <time.h>
#define NUM_THREADS 4
#define NUM_OPS 1000000
// 共享资源
int shared_counter = 0;
pthread_mutex_t mutex;
pthread_spinlock_t spinlock;
// 互斥锁线程函数
void *mutex_thread(void *arg) {
for (int i = 0; i < NUM_OPS; i++) {
pthread_mutex_lock(&mutex);
shared_counter++;
pthread_mutex_unlock(&mutex);
}
return NULL;
}
// 自旋锁线程函数
void *spin_thread(void *arg) {
for (int i = 0; i < NUM_OPS; i++) {
pthread_spin_lock(&spinlock);
shared_counter++;
pthread_spin_unlock(&spinlock);
}
return NULL;
}
double get_time_ms(struct timespec *start, struct timespec *end) {
return (end->tv_sec - start->tv_sec) * 1000.0 +
(end->tv_nsec - start->tv_nsec) / 1000000.0;
}
int main() {
pthread_t threads[NUM_THREADS];
struct timespec start, end;
double mutex_time, spin_time;
// 初始化
pthread_mutex_init(&mutex, NULL);
pthread_spin_init(&spinlock, PTHREAD_PROCESS_PRIVATE);
// 测试互斥锁
shared_counter = 0;
clock_gettime(CLOCK_MONOTONIC, &start);
for (int i = 0; i < NUM_THREADS; i++) {
pthread_create(&threads[i], NULL, mutex_thread, NULL);
}
for (int i = 0; i < NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
}
clock_gettime(CLOCK_MONOTONIC, &end);
mutex_time = get_time_ms(&start, &end);
printf("Mutex: counter=%d, time=%.2f ms\n", shared_counter, mutex_time);
// 测试自旋锁
shared_counter = 0;
clock_gettime(CLOCK_MONOTONIC, &start);
for (int i = 0; i < NUM_THREADS; i++) {
pthread_create(&threads[i], NULL, spin_thread, NULL);
}
for (int i = 0; i < NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
}
clock_gettime(CLOCK_MONOTONIC, &end);
spin_time = get_time_ms(&start, &end);
printf("Spinlock: counter=%d, time=%.2f ms\n", shared_counter, spin_time);
// 清理
pthread_mutex_destroy(&mutex);
pthread_spin_destroy(&spinlock);
printf("Spinlock is %.2fx faster than mutex\n", mutex_time / spin_time);
return 0;
}
编译运行:
gcc -pthread -o lock_compare lock_compare.c
./lock_compare
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>
// 模拟中断共享数据
volatile int sensor_data = 0;
pthread_spinlock_t irq_spinlock;
// 模拟中断处理函数(在真实嵌入式系统中这是ISR)
void irq_handler(int sig) {
// 在中断处理函数中使用自旋锁保护共享数据
pthread_spin_lock(&irq_spinlock);
// 更新传感器数据
sensor_data++;
pthread_spin_unlock(&irq_spinlock);
}
// 主线程:读取传感器数据
void *sensor_reader(void *arg) {
int last_value = 0;
for (int i = 0; i < 20; i++) {
pthread_spin_lock(&irq_spinlock);
int current_value = sensor_data;
pthread_spin_unlock(&irq_spinlock);
if (current_value != last_value) {
printf("[Reader] Sensor value changed: %d -> %d\n",
last_value, current_value);
last_value = current_value;
}
usleep(100000); // 100ms
}
return NULL;
}
int main() {
pthread_t reader_thread;
// 初始化自旋锁
pthread_spin_init(&irq_spinlock, PTHREAD_PROCESS_PRIVATE);
// 注册信号处理函数(模拟中断)
struct sigaction sa;
sa.sa_handler = irq_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGUSR1, &sa, NULL);
// 创建读线程
pthread_create(&reader_thread, NULL, sensor_reader, NULL);
// 模拟中断触发
for (int i = 0; i < 10; i++) {
usleep(50000); // 50ms
kill(getpid(), SIGUSR1); // 发送信号模拟中断
}
// 等待读线程完成
pthread_join(reader_thread, NULL);
// 销毁自旋锁
pthread_spin_destroy(&irq_spinlock);
printf("Final sensor value: %d\n", sensor_data);
return 0;
}
编译运行:
gcc -pthread -o irq_spinlock irq_spinlock.c
./irq_spinlock
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#define MAX_CONFIG_ENTRIES 10
// 配置结构体
typedef struct {
char key[64];
char value[128];
} ConfigEntry;
// 配置管理器
typedef struct {
ConfigEntry entries[MAX_CONFIG_ENTRIES];
int count;
pthread_rwlock_t rwlock;
} ConfigManager;
// 初始化配置管理器
void config_init(ConfigManager *mgr) {
mgr->count = 0;
pthread_rwlock_init(&mgr->rwlock, NULL);
// 添加一些默认配置
strcpy(mgr->entries[0].key, "device_name");
strcpy(mgr->entries[0].value, "Embedded_System_v1.0");
strcpy(mgr->entries[1].key, "ip_address");
strcpy(mgr->entries[1].value, "192.168.1.100");
strcpy(mgr->entries[2].key, "port");
strcpy(mgr->entries[2].value, "8080");
mgr->count = 3;
}
// 读取配置(频繁调用)
const char *config_get(ConfigManager *mgr, const char *key) {
pthread_rwlock_rdlock(&mgr->rwlock);
for (int i = 0; i < mgr->count; i++) {
if (strcmp(mgr->entries[i].key, key) == 0) {
const char *value = mgr->entries[i].value;
pthread_rwlock_unlock(&mgr->rwlock);
return value;
}
}
pthread_rwlock_unlock(&mgr->rwlock);
return NULL;
}
// 更新配置(偶尔调用)
int config_set(ConfigManager *mgr, const char *key, const char *value) {
pthread_rwlock_wrlock(&mgr->rwlock);
// 查找已存在的key
for (int i = 0; i < mgr->count; i++) {
if (strcmp(mgr->entries[i].key, key) == 0) {
strcpy(mgr->entries[i].value, value);
pthread_rwlock_unlock(&mgr->rwlock);
return 0; // 更新成功
}
}
// 添加新key
if (mgr->count < MAX_CONFIG_ENTRIES) {
strcpy(mgr->entries[mgr->count].key, key);
strcpy(mgr->entries[mgr->count].value, value);
mgr->count++;
pthread_rwlock_unlock(&mgr->rwlock);
return 0; // 添加成功
}
pthread_rwlock_unlock(&mgr->rwlock);
return -1; // 配置已满
}
// 销毁配置管理器
void config_destroy(ConfigManager *mgr) {
pthread_rwlock_destroy(&mgr->rwlock);
}
// 读线程:频繁读取配置
void *reader_thread(void *arg) {
ConfigManager *mgr = (ConfigManager *)arg;
for (int i = 0; i < 20; i++) {
const char *name = config_get(mgr, "device_name");
const char *ip = config_get(mgr, "ip_address");
const char *port = config_get(mgr, "port");
printf("[Reader] device=%s, ip=%s, port=%s\n",
name ? name : "N/A",
ip ? ip : "N/A",
port ? port : "N/A");
usleep(100000); // 100ms
}
return NULL;
}
// 写线程:偶尔更新配置
void *writer_thread(void *arg) {
ConfigManager *mgr = (ConfigManager *)arg;
for (int i = 0; i < 5; i++) {
usleep(500000); // 500ms
char value[128];
sprintf(value, "192.168.1.%d", 100 + i);
config_set(mgr, "ip_address", value);
printf("[Writer] Updated ip_address to %s\n", value);
}
return NULL;
}
int main() {
ConfigManager mgr;
pthread_t reader, writer;
config_init(&mgr);
// 创建线程
pthread_create(&reader, NULL, reader_thread, &mgr);
pthread_create(&writer, NULL, writer_thread, &mgr);
// 等待线程完成
pthread_join(reader, NULL);
pthread_join(writer, NULL);
config_destroy(&mgr);
printf("Config test completed.\n");
return 0;
}
编译运行:
gcc -pthread -o config_rwlock config_rwlock.c
./config_rwlock
| 问题 | 描述 | 解决方案 |
|---|---|---|
| 写线程饥饿 | 读线程持续持有锁,写线程无法获取 | 设置写优先属性 |
| 锁升级死锁 | 持有读锁时尝试获取写锁 | 先释放读锁,再获取写锁 |
| 锁降级风险 | 持有写锁时直接降级为读锁 | 可能导致数据不一致 |
| 未初始化 | 使用未初始化的读写锁 | 确保调用pthread_rwlock_init |
| 未销毁 | 程序结束时未销毁读写锁 | 使用pthread_rwlock_destroy |
| 问题 | 描述 | 解决方案 |
|---|---|---|
| CPU浪费 | 长时间自旋导致CPU空转 | 确保临界区足够短 |
| 死锁 | 在单CPU系统上自旋时被中断 | 中断处理函数中使用自旋锁时需禁用中断 |
| 优先级反转 | 低优先级线程持有自旋锁,高优先级线程自旋 | 使用优先级继承机制 |
| 不可嵌套 | 自旋锁不支持递归获取 | 使用pthread_spin_lock前检查锁状态 |
| 中断上下文 | 中断处理函数不能使用互斥锁 | 必须使用自旋锁 |
中断处理:
// 中断处理函数中使用自旋锁
void irq_handler(void) {
// 禁用本地中断(如果是单核系统)
// spin_lock_irqsave(&lock, flags);
pthread_spin_lock(&spinlock);
// 访问共享数据
pthread_spin_unlock(&spinlock);
// 恢复中断状态
// spin_unlock_irqrestore(&lock, flags);
}
配置数据:
// 读多写少场景使用读写锁
const char *get_config(const char *key) {
pthread_rwlock_rdlock(&config_lock);
// 读取配置
pthread_rwlock_unlock(&config_lock);
return value;
}
void update_config(const char *key, const char *value) {
pthread_rwlock_wrlock(&config_lock);
// 更新配置
pthread_rwlock_unlock(&config_lock);
}
答:
答:
pthread_rwlockattr_setkind_np()设置写优先答:
| 特性 | 自旋锁 | 互斥锁 |
|---|---|---|
| 等待方式 | 忙等待(占用CPU) | 睡眠等待(不占用CPU) |
| 上下文切换 | 无 | 有 |
| 适用场景 | 锁持有时间极短 | 锁持有时间较长 |
| 单CPU | 不适合 | 适合 |
| 中断上下文 | 可以使用 | 不能使用 |
答:
答:
pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP)答:
答: