您好,登录后才能下订单哦!
# Linux中线程互斥锁的示例分析
## 1. 线程安全与互斥锁基础概念
### 1.1 多线程环境下的共享资源问题
在现代操作系统中,多线程编程已成为提高程序性能的重要手段。然而当多个线程并发访问共享资源时,会出现**竞态条件(Race Condition)**问题:
```c
// 典型的多线程计数器问题
int counter = 0;
void* increment(void* arg) {
for (int i = 0; i < 100000; i++) {
counter++; // 非原子操作
}
return NULL;
}
上述代码中,counter++
操作实际上包含三个步骤:
1. 从内存读取counter值到寄存器
2. 寄存器值加1
3. 将结果写回内存
当两个线程同时执行这些步骤时,可能导致最终结果小于预期值(如200000次操作可能得到150000)。
互斥锁(Mutex)是解决这类问题的同步原语,其核心特性包括: - 原子性:锁的获取和释放操作是不可分割的 - 排他性:同一时刻只有一个线程能持有锁 - 阻塞机制:未获取锁的线程会进入等待状态
Linux中的互斥锁通过内核提供的futex(快速用户空间互斥锁)机制实现,在无竞争情况下完全在用户空间操作,竞争激烈时才进入内核等待队列。
#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_mutexattr_t
可以定制锁行为:
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
// 设置锁类型
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); // 可重入锁
// 设置进程共享属性
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); // 进程间共享
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, &attr);
常见锁类型:
- PTHREAD_MUTEX_NORMAL
:标准锁,不检测死锁
- PTHREAD_MUTEX_ERRORCHECK
:提供错误检查
- PTHREAD_MUTEX_RECURSIVE
:允许同一线程重复加锁
#include <stdio.h>
#include <pthread.h>
typedef struct {
int balance;
pthread_mutex_t lock;
} BankAccount;
void init_account(BankAccount *acc, int initial_balance) {
acc->balance = initial_balance;
pthread_mutex_init(&acc->lock, NULL);
}
void deposit(BankAccount *acc, int amount) {
pthread_mutex_lock(&acc->lock);
acc->balance += amount;
pthread_mutex_unlock(&acc->lock);
}
int withdraw(BankAccount *acc, int amount) {
pthread_mutex_lock(&acc->lock);
int success = 0;
if (acc->balance >= amount) {
acc->balance -= amount;
success = 1;
}
pthread_mutex_unlock(&acc->lock);
return success;
}
#define BUFFER_SIZE 10
typedef struct {
int buffer[BUFFER_SIZE];
int count;
int in;
int out;
pthread_mutex_t mutex;
pthread_cond_t not_empty;
pthread_cond_t not_full;
} PCBuffer;
void init_buffer(PCBuffer *b) {
b->count = b->in = b->out = 0;
pthread_mutex_init(&b->mutex, NULL);
pthread_cond_init(&b->not_empty, NULL);
pthread_cond_init(&b->not_full, NULL);
}
void produce(PCBuffer *b, int item) {
pthread_mutex_lock(&b->mutex);
while (b->count == BUFFER_SIZE) {
pthread_cond_wait(&b->not_full, &b->mutex);
}
b->buffer[b->in] = item;
b->in = (b->in + 1) % BUFFER_SIZE;
b->count++;
pthread_cond_signal(&b->not_empty);
pthread_mutex_unlock(&b->mutex);
}
int consume(PCBuffer *b) {
pthread_mutex_lock(&b->mutex);
while (b->count == 0) {
pthread_cond_wait(&b->not_empty, &b->mutex);
}
int item = b->buffer[b->out];
b->out = (b->out + 1) % BUFFER_SIZE;
b->count--;
pthread_cond_signal(&b->not_full);
pthread_mutex_unlock(&b->mutex);
return item;
}
锁的粒度选择直接影响并发性能:
// 全局单个锁
pthread_mutex_t global_lock;
void access_all_resources() {
pthread_mutex_lock(&global_lock);
// 操作所有共享资源
pthread_mutex_unlock(&global_lock);
}
// 为每个资源单独加锁
typedef struct {
int data;
pthread_mutex_t lock;
} Resource;
Resource res[N];
void access_resource(int i) {
pthread_mutex_lock(&res[i].lock);
// 操作单个资源
pthread_mutex_unlock(&res[i].lock);
}
常见死锁场景及解决方案:
// 线程1
lock(A);
lock(B);
// 线程2
lock(B);
lock(A); // 可能导致死锁
解决方案:固定锁的获取顺序
void foo() {
lock(M);
bar();
unlock(M);
}
void bar() {
lock(M); // 如果不是递归锁会死锁
// ...
unlock(M);
}
解决方案:使用PTHREAD_MUTEX_RECURSIVE
属性
线程数 | 无锁(错误) | 全局锁(ms) | 分段锁(ms) | 自旋锁(ms) |
---|---|---|---|---|
2 | 153,291 | 45 | 32 | 28 |
4 | 287,501 | 89 | 47 | 42 |
8 | 512,837 | 175 | 78 | 65 |
注:测试为1000万次计数器递增操作
错误1:忘记释放锁
void critical_section() {
pthread_mutex_lock(&mutex);
if (error_condition) {
return; // 直接返回导致锁未释放
}
pthread_mutex_unlock(&mutex);
}
解决方法:使用RI模式或__attribute__((cleanup))
void auto_unlock(pthread_mutex_t **mutex) {
pthread_mutex_unlock(*mutex);
}
void critical_section() {
pthread_mutex_t *mutex __attribute__((cleanup(auto_unlock))) = &the_mutex;
pthread_mutex_lock(&the_mutex);
// ...
}
错误2:错误使用递归锁
void recursive_func(int n) {
pthread_mutex_lock(&mutex); // 普通锁重复获取
if (n > 0) {
recursive_func(n-1);
}
pthread_mutex_unlock(&mutex);
}
适用场景:读多写少
#include <pthread.h>
pthread_rwlock_t rwlock;
void reader() {
pthread_rwlock_rdlock(&rwlock);
// 读取共享数据
pthread_rwlock_unlock(&rwlock);
}
void writer() {
pthread_rwlock_wrlock(&rwlock);
// 修改共享数据
pthread_rwlock_unlock(&rwlock);
}
适用场景:临界区非常短且不希望线程休眠
pthread_spinlock_t spinlock;
void fast_path() {
pthread_spin_lock(&spinlock);
// 极短的操作
pthread_spin_unlock(&spinlock);
}
if (pthread_mutex_lock(&mutex) != 0) {
perror("pthread_mutex_lock");
// 错误处理
}
class LockGuard {
public:
LockGuard(pthread_mutex_t &m) : mutex(m) {
pthread_mutex_lock(&mutex);
}
~LockGuard() {
pthread_mutex_unlock(&mutex);
}
private:
pthread_mutex_t &mutex;
};
helgrind
:检测数据竞争mutrace
:分析锁争用情况/* 完整的多线程安全队列实现 */
#include <stdlib.h>
#include <pthread.h>
typedef struct {
int *array;
int capacity;
int size;
int front;
int rear;
pthread_mutex_t lock;
pthread_cond_t not_empty;
pthread_cond_t not_full;
} ThreadSafeQueue;
ThreadSafeQueue* queue_create(int capacity) {
ThreadSafeQueue *q = malloc(sizeof(ThreadSafeQueue));
q->array = malloc(sizeof(int) * capacity);
q->capacity = capacity;
q->size = 0;
q->front = 0;
q->rear = -1;
pthread_mutex_init(&q->lock, NULL);
pthread_cond_init(&q->not_empty, NULL);
pthread_cond_init(&q->not_full, NULL);
return q;
}
void queue_enqueue(ThreadSafeQueue *q, int item) {
pthread_mutex_lock(&q->lock);
while (q->size == q->capacity) {
pthread_cond_wait(&q->not_full, &q->lock);
}
q->rear = (q->rear + 1) % q->capacity;
q->array[q->rear] = item;
q->size++;
pthread_cond_signal(&q->not_empty);
pthread_mutex_unlock(&q->lock);
}
int queue_dequeue(ThreadSafeQueue *q) {
pthread_mutex_lock(&q->lock);
while (q->size == 0) {
pthread_cond_wait(&q->not_empty, &q->lock);
}
int item = q->array[q->front];
q->front = (q->front + 1) % q->capacity;
q->size--;
pthread_cond_signal(&q->not_full);
pthread_mutex_unlock(&q->lock);
return item;
}
通过本文的详细分析和示例,读者应能全面掌握Linux线程互斥锁的使用方法和最佳实践。在实际开发中,应根据具体场景选择合适的同步策略,并始终注意线程安全和性能的平衡。 “`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。