🔐 Linux中的同步和异步锁
在多线程/多进程编程中,锁 (Lock) 是用于控制访问共享资源的机制,防止数据竞争和一致性问题。Linux 提供了多种锁机制,既包括同步锁也包括异步锁,用于满足不同场景下的并发控制需求。
📌 一、同步锁 (Synchronous Lock)
同步锁要求线程在获取锁时必须等待,直到锁被释放。适用于以下场景:
✅ 线程/进程需按序执行;
✅ 共享资源的访问需确保一致性;
✅ 防止竞态条件;
🔎 常见的同步锁类型
锁类型 | 描述 |
---|---|
互斥锁 (Mutex) | 最常用的同步锁,确保同一时刻仅有一个线程访问共享资源; |
信号量 (Semaphore) | 允许多个线程同时访问指定数量的资源; |
读写锁 (Read-Write Lock) | 提供更高效的读操作,允许多个读线程并行执行; |
条件变量 (Condition Variable) | 配合互斥锁,线程需满足某个条件才能继续执行; |
自旋锁 (Spinlock) | 线程在等待锁时会持续循环检查锁状态,适用于短时间临界区; |
🔥 1. 互斥锁 (Mutex) 示例
pthread_mutex
是 Linux 中最常用的互斥锁。
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
pthread_mutex_t lock; // 定义一个互斥锁
int counter = 0;
void* thread_function(void* arg) {
pthread_mutex_lock(&lock); // 加锁
counter++;
printf("线程 %ld, counter = %d\n", pthread_self(), counter);
sleep(1); // 模拟耗时任务
pthread_mutex_unlock(&lock); // 解锁
return NULL;
}
int main() {
pthread_t t1, t2;
pthread_mutex_init(&lock, NULL); // 初始化锁
pthread_create(&t1, NULL, thread_function, NULL);
pthread_create(&t2, NULL, thread_function, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_mutex_destroy(&lock); // 销毁锁
return 0;
}
✅ 输出示例
线程 139889467115264, counter = 1
线程 139889458722560, counter = 2
🔎 互斥锁可防止多个线程同时修改
counter
,确保数据安全。
🔥 2. 信号量 (Semaphore) 示例
信号量用于控制同时访问资源的最大线程数。
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <unistd.h>
sem_t semaphore; // 定义信号量
void* thread_function(void* arg) {
sem_wait(&semaphore); // 获取信号量
printf("线程 %ld 正在访问资源\n", pthread_self());
sleep(2);
printf("线程 %ld 释放资源\n", pthread_self());
sem_post(&semaphore); // 释放信号量
return NULL;
}
int main() {
pthread_t threads[5];
sem_init(&semaphore, 0, 2); // 信号量初始值为 2(允许最多 2 个线程同时访问)
for (int i = 0; i < 5; i++) {
pthread_create(&threads[i], NULL, thread_function, NULL);
}
for (int i = 0; i < 5; i++) {
pthread_join(threads[i], NULL);
}
sem_destroy(&semaphore); // 销毁信号量
return 0;
}
✅ 输出示例
线程 139889467115264 正在访问资源
线程 139889458722560 正在访问资源
线程 139889467115264 释放资源
线程 139889458722560 释放资源
线程 139889450329856 正在访问资源
...
🔎 信号量确保了同时只有 2 个线程访问共享资源。
🔥 3. 读写锁 (Read-Write Lock) 示例
pthread_rwlock
可提高多读场景下的性能。
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
pthread_rwlock_t rwlock; // 定义读写锁
int counter = 0;
void* reader(void* arg) {
pthread_rwlock_rdlock(&rwlock); // 获取读锁
printf("读线程 %ld: counter = %d\n", pthread_self(), counter);
pthread_rwlock_unlock(&rwlock); // 释放读锁
return NULL;
}
void* writer(void* arg) {
pthread_rwlock_wrlock(&rwlock); // 获取写锁
counter++;
printf("写线程 %ld: counter = %d\n", pthread_self(), counter);
pthread_rwlock_unlock(&rwlock); // 释放写锁
return NULL;
}
int main() {
pthread_t readers[3], writer_thread;
pthread_rwlock_init(&rwlock, NULL); // 初始化读写锁
for (int i = 0; i < 3; i++) {
pthread_create(&readers[i], NULL, reader, NULL);
}
pthread_create(&writer_thread, NULL, writer, NULL);
for (int i = 0; i < 3; i++) {
pthread_join(readers[i], NULL);
}
pthread_join(writer_thread, NULL);
pthread_rwlock_destroy(&rwlock); // 销毁读写锁
return 0;
}
✅ 输出示例
读线程 139889467115264: counter = 0
读线程 139889458722560: counter = 0
写线程 139889450329856: counter = 1
🔎 读锁可并发读取,而写锁是独占的。
📌 二、异步锁 (Asynchronous Lock)
异步锁不要求线程/进程等待锁的释放,而是使用回调、通知机制等来处理并发操作。
🔎 常见的异步锁类型
锁类型 | 描述 |
---|---|
fcntl 锁 | 非阻塞锁,支持多进程文件锁; |
异步 I/O (AIO) | 通过 io_submit() 、io_getevents() 等实现; |
Epoll/Select/Poll | 非阻塞 I/O 事件监听; |
信号 (Signal) | 使用 sigaction() 、kill() 等方法触发异步回调; |
🔥 fcntl 异步锁 (非阻塞锁) 示例
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main() {
int fd = open("file.txt", O_WRONLY | O_CREAT, 0666);
struct flock lock;
lock.l_type = F_WRLCK; // 写锁
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0; // 锁住整个文件
if (fcntl(fd, F_SETLK, &lock) == -1) {
perror("获取锁失败");
return 1;
}
printf("成功获取锁!\n");
sleep(5); // 模拟长时间任务
lock.l_type = F_UNLCK;
fcntl(fd, F_SETLK, &lock); // 释放锁
printf("释放锁\n");
close(fd);
return 0;
}
✅ 输出示例
成功获取锁!
释放锁
🔎
fcntl
锁不会阻塞线程,而是立即返回,适用于非阻塞 I/O。
🚀 总结
特点 | 同步锁 | 异步锁 |
---|---|---|
等待机制 | 必须等待锁释放才能继续执行; | 不需等待,采用回调/事件机制; |
使用场景 | 资源竞争频繁,数据一致性要求高; | I/O 密集型、异步任务; |
性能 | 容易阻塞线程,效率可能较低; | 更加高效,减少线程阻塞; |
常见类型 | Mutex、Semaphore、读写锁; | fcntl 锁、AIO、epoll 等; |