1.介绍
进程同步是现代操作系统和并发编程的关键方面。它确保多个进程或线程可以安全地访问共享资源,而不会导致竞争条件、死锁或其他同步问题。高级同步技术超越了基本的锁和信号量,提供更高效和可扩展的解决方案。
- 互斥:确保一次只有一个进程可以访问共享资源。
- 进展:保证进程最终会取得进展,而不会挨饿。
- 有界等待:限制进程必须等待多长时间才能访问资源。
- 公平性:确保所有进程都有公平的机会访问资源。
2. 彼得森算法
Peterson算法是一种经典解决方案,用于在两个进程之间实现互斥,而不依赖于硬件原子指令。它使用两个共享变量:flag和turn。
埃特森算法实现
以下是Peterson算法在C语言中的实现:
#include <stdbool.h>
#include <stdio.h>
#include <pthread.h>
typedef struct {
bool flag[2];
int turn;
} peterson_t;
void peterson_init(peterson_t *p) {
p->flag[0] = p->flag[1] = false;
p->turn = 0;
}
void peterson_enter_critical(peterson_t *p, int process_id) {
int other = 1 - process_id;
p->flag[process_id] = true;
p->turn = other;
// Memory barrier to ensure visibility of flag and turn
__sync_synchronize();
while (p->flag[other] && p->turn == other) {
// Busy wait
__sync_synchronize();
}
}
void peterson_exit_critical(peterson_t *p, int process_id) {
__sync_synchronize();
p->flag[process_id] = false;
}
// Example usage
void *process_function(void *arg) {
peterson_t *p = ((struct thread_args*)arg)->peterson;
int id = ((struct thread_args*)arg)->id;
for (int i = 0; i < 1000; i++) {
peterson_enter_critical(p, id);
// Critical section
printf("Process %d in critical section\n", id);
peterson_exit_critical(p, id);
}
return NULL;
}
3. 高级同步原语
高级同步原语,如信号量和读写锁,提供了更复杂的机制来管理对共享资源的并发访问。
信号量实现
以下是信号量的简化实现:
typedef struct {
atomic_int value;
struct waiting_thread *waiters;
} semaphore_t;
void semaphore_init(semaphore_t *sem, int initial_value) {
atomic_init(&sem->value, initial_value);
sem->waiters = NULL;
}
bool semaphore_wait(semaphore_t *sem, unsigned long timeout) {
while (true) {
int current = atomic_load(&sem->value);
if (current <= 0) {
if (timeout == 0) return false;
// Add to waiters list
add_to_waiters(sem);
wait_for_signal(timeout);
continue;
}
if (atomic_compare_exchange_strong(&sem->value,
¤t, current - 1)) {
return true;
}
}
}
void semaphore_post(semaphore_t *sem) {
int current = atomic_fetch_add(&sem->value, 1);
if (current < 0) {
// Wake up one waiter
wake_one_waiter(sem);
}
}
4. 无锁编程概念
无锁编程旨在在不使用传统锁的情况下实现并发,这可能会导致性能瓶颈和死锁。相反,它依赖于原子操作和精细的内存管理。
无锁队列实现
以下是实现一个无锁队列的示例:
typedef struct node {
void *data;
struct node *next;
} node_t;
typedef struct {
atomic_ptr head;
atomic_ptr tail;
} lock_free_queue_t;
void queue_init(lock_free_queue_t *queue) {
node_t *dummy = malloc(sizeof(node_t));
dummy->next = NULL;
atomic_store(&queue->head, dummy);
atomic_store(&queue->tail, dummy);
}
void queue_enqueue(lock_free_queue_t *queue, void *data) {
node_t *new_node = malloc(sizeof(node_t));
new_node->data = data;
new_node->next = NULL;
while (1) {
node_t *tail = atomic_load(&queue->tail);
node_t *next = atomic_load(&tail->next);
if (tail == atomic_load(&queue->tail)) {
if (next == NULL) {
if (atomic_compare_exchange_weak(&tail->next,
&next, new_node)) {
atomic_compare_exchange_weak(&queue->tail,
&tail, new_node);
return;
}
} else {
atomic_compare_exchange_weak(&queue->tail,
&tail, next);
}
}
}
}
5. 内存屏障和内存排序
内存屏障用于强制对内存操作进行排序约束,确保某些操作在完成其他操作之前完成。
内存屏障实现
typedef struct {
atomic_int flag;
atomic_int turn;
} memory_fence_sync_t;
void memory_fence_example(memory_fence_sync_t *sync, int thread_id) {
// Store-Store barrier
atomic_store_explicit(&sync->flag, 1, memory_order_release);
// Full memory barrier
atomic_thread_fence(memory_order_seq_cst);
// Load-Load barrier
int value = atomic_load_explicit(&sync->turn, memory_order_acquire);
// Example of proper ordering
if (value == thread_id) {
// Critical section
atomic_store_explicit(&sync->flag, 0, memory_order_release);
}
}
6. 高级互斥实现
高级互斥锁实现,如递归互斥锁,允许线程多次获取同一个互斥锁,而不会导致死锁。
递归互斥锁实现
以下是递归互斥锁的实现:
typedef struct {
atomic_int lock;
atomic_int recursion_count;
atomic_tid owner;
spinlock_t wait_lock;
struct list_head wait_list;
} recursive_mutex_t;
int recursive_mutex_lock(recursive_mutex_t *mutex) {
tid_t current = get_current_tid();
tid_t owner = atomic_load(&mutex->owner);
if (owner == current) {
atomic_fetch_add(&mutex->recursion_count, 1);
return 0;
}
while (1) {
if (atomic_compare_exchange_strong(&mutex->lock,
&owner, current)) {
atomic_store(&mutex->owner, current);
atomic_store(&mutex->recursion_count, 1);
return 0;
}
// Add to wait list and sleep
spin_lock(&mutex->wait_lock);
add_to_wait_list(&mutex->wait_list, current);
spin_unlock(&mutex->wait_lock);
schedule();
}
}
-
递归锁定:
recursive_mutex_lock()函数允许线程通过增加递归计数多次获取互斥锁。 -
等待列表: 如果互斥锁已被其他线程持有,当前线程将被添加到等待列表中,并进入睡眠状态,直到互斥锁可用。
7. 读-写锁机制
读-写锁允许多个读者同时访问资源,但确保写者具有独占访问权限。
公平读写锁实现
以下是公平读-写锁的实现:
typedef struct {
atomic_int readers;
atomic_int writers;
atomic_int waiting_readers;
atomic_int waiting_writers;
spinlock_t lock;
struct list_head reader_wait_list;
struct list_head writer_wait_list;
} fair_rw_lock_t;
int fair_rw_lock_read_lock(fair_rw_lock_t *rwlock) {
spin_lock(&rwlock->lock);
if (atomic_load(&rwlock->writers) == 0 &&
atomic_load(&rwlock->waiting_writers) == 0) {
atomic_fetch_add(&rwlock->readers, 1);
spin_unlock(&rwlock->lock);
return 0;
}
atomic_fetch_add(&rwlock->waiting_readers, 1);
add_to_wait_list(&rwlock->reader_wait_list, current);
spin_unlock(&rwlock->lock);
schedule();
return 0;
}
- 读锁 当没有写者活动时,
fair_rw_lock_read_lock()函数允许多个读者获取锁。 - 等待列表: 如果有写者等待,则将读者添加到等待列表中,并等待锁可用。
8. 优先级继承协议
优先级继承是一种用于防止优先级反转的技术,其中高优先级任务被低优先级任务阻塞,而低优先级任务持有共享资源。
优先级继承实现
以下是优先级继承的实现:
typedef struct {
atomic_int lock;
atomic_int original_priority;
atomic_int inherited_priority;
atomic_tid owner;
struct list_head waiters;
} pi_mutex_t;
int pi_mutex_lock(pi_mutex_t *mutex) {
tid_t current = get_current_tid();
int current_priority = get_thread_priority(current);
while (1) {
tid_t owner = atomic_load(&mutex->owner);
if (owner == 0) {
if (atomic_compare_exchange_strong(&mutex->lock,
&owner, 1)) {
atomic_store(&mutex->owner, current);
atomic_store(&mutex->original_priority,
current_priority);
return 0;
}
} else {
// Check for priority inheritance
int owner_priority = get_thread_priority(owner);
if (current_priority > owner_priority) {
set_thread_priority(owner, current_priority);
atomic_store(&mutex->inherited_priority,
current_priority);
}
// Add to waiters and sleep
add_to_waiters(mutex, current, current_priority);
schedule();
}
}
}
9. 性能分析
过程同步的关键性能指标包括:
- 锁竞争率: 衡量线程竞争锁的频率。
- 优先级反转持续时间: 在优先级反转情况下的时间消耗。
- 上下文切换开销: 同步期间线程切换的成本。
- 缓存行弹跳: 同步对缓存一致性的影响。
10. 结论
高级进程同步机制对于构建高效和可靠的并发系统至关重要。理解这些概念及其实现对于开发高性能多线程应用程序是必不可少的。