Linux 自旋锁(spinlock)是一种用于保护共享资源的锁机制,常用于多核处理器中,在某个核(或线程)试图获取锁时,如果发现锁已被其他核持有,它会忙等(不断循环检查)而不是让出 CPU 时间片。
自旋锁的特点是适用于锁的持有时间非常短的场景,因为它在等待期间不会主动放弃 CPU,而是不断尝试获取锁,这在多核系统中可以避免由于线程调度带来的上下文切换开销。
工作原理:
自旋锁常用于以下情况:
自旋锁与互斥锁的比较如下:
实现方式上的区别:
开销上的区别:
适用场景上的区别:
死锁问题:
1
自旋锁初始化与销毁
自旋锁需要在使用前进行初始化,并在不再使用时销毁。
初始化自旋锁函数如下:
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
参数:
返回值:成功时返回 0,失败时返回非零错误码。
销毁自旋锁函数如下:
int pthread_spin_destroy(pthread_spinlock_t *lock);
参数:lock:指向要销毁的自旋锁对象。
返回值:成功时返回 0,失败时返回非零错误码。
2
自旋锁加锁与解锁
加锁函数如下:
int pthread_spin_lock(pthread_spinlock_t *lock);
参数:lock:指向要加锁的自旋锁对象。
返回值:成功时返回 0;如果锁已经被其他线程占用,则线程会忙等,直到成功获取锁,最终返回 0。
尝试加锁函数如下:
int pthread_spin_trylock(pthread_spinlock_t *lock);
参数:lock:指向要加锁的自旋锁对象。
返回值:
解锁函数如下:
int pthread_spin_unlock(pthread_spinlock_t *lock);
参数:lock:指向要解锁的自旋锁对象。
返回值:成功时返回 0,失败时返回非零错误码。
下面是一个完整的示例,展示如何使用自旋锁,包括初始化、加锁、解锁和销毁:
pthread_spinlock_t spinlock; // 定义自旋锁
int shared_data = 0; // 共享数据
void *thread_func(void *arg) {
pthread_spin_lock(&spinlock); // 加锁
shared_data++;
printf("Thread %ld: shared_data = %d\n", (long)arg, shared_data);
pthread_spin_unlock(&spinlock); // 解锁
return NULL;
}
int main() {
pthread_t threads[2];
// 初始化自旋锁
if (pthread_spin_init(&spinlock, PTHREAD_PROCESS_PRIVATE) != 0) {
perror("Failed to initialize spinlock");
return 1;
}
// 创建两个线程
pthread_create(&threads[0], NULL, thread_func, (void *)1);
pthread_create(&threads[1], NULL, thread_func, (void *)2);
// 等待线程结束
pthread_join(threads[0], NULL);
pthread_join(threads[1], NULL);
// 销毁自旋锁
if (pthread_spin_destroy(&spinlock) != 0) {
perror("Failed to destroy spinlock");
return 1;
}
return 0;
}
自旋锁的主要问题在于,它在获取不到锁时不会释放 CPU,而是持续消耗资源。
如果锁持有时间较长,CPU 的利用效率会急剧下降。
因此,自旋锁不适合用于长时间锁定的场景,只适合那些临界区极短的操作。
通过对 pthread_spin_* 函数的合理使用,可以有效管理多线程访问共享资源的同步问题。
确保在适当的地方进行加锁和解锁,以防止死锁和资源竞争。