点击↑深色口袋物联,选择关注公众号,获取更多内容,不迷路
在glibc2.35中,pthread线程提供了5种同步机制,分别为条件变量、互斥锁、读写锁、自旋锁和屏障,本篇主要回顾自旋锁
在glibc2.35中,线程自旋锁的定义在sysdeps/nptl/bits/pthreadtypes.h中pthread_spinlock_t
typedef volatile int pthread_spinlock_t;即一个易变的整数,因为自旋锁只有两种状态,0(解锁)和 1(加锁),它不需要像互斥锁那样记录“所有者线程 ID”、“递归计数”或“优先级继承”等复杂信息,一个整数位(或字节)就足够表示状态了,而volatile告诉编译器:“不要对这个变量进行优化,每次读取都必须从内存中直接读取,每次写入都必须立即写入内存。”
自旋锁的实现完全依赖原子操作指令(如 ARM 的 LDREX/STREX),它完全运行在用户态,不涉及系统调用,不进入内核。
自旋锁的核心作用:在「锁持有时间极短」的场景下,以「CPU 忙等」的方式,避免线程阻塞 / 唤醒的内核态切换开销,实现极致的加解锁性能
互斥锁 / 读写锁的核心问题是「阻塞开销」:
count++),线程阻塞 / 唤醒的开销(μs 级)远大于锁持有时间,导致性能暴跌;自旋锁的核心价值是**「无上下文切换开销」**,所有场景都围绕「极短时间锁持有 + 低竞争 + 多核 CPU」展开,仅占 Linux 同步场景的 5% 左右,但在匹配场景下性能无可替代,按使用频率排序如下:
pthread_spin_trylock非阻塞加锁,失败则立即重试,避免线程阻塞;自旋锁的接口全部在 <pthread.h> 头文件中,编译时必须加 -lpthread 链接线程库,接口数量极少、逻辑简单,所有接口返回值:成功返回 0,失败返回错误码(非 errno,需手动处理)。
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);锁的共享范围(核心参数):
PTHREAD_PROCESS_PRIVATE(0):仅当前进程内的线程共享(99% 场景用此值);PTHREAD_PROCESS_SHARED(1):跨进程共享(如多进程共享内存场景);
注意:自旋锁无静态初始化方式,必须调用
pthread_spin_init初始化!
int pthread_spin_destroy(pthread_spinlock_t *lock);pthread_spin_init成对调用;② 锁被持有期间不能销毁(返回 EBUSY 错误)。int pthread_spin_lock(pthread_spinlock_t *lock);int pthread_spin_trylock(pthread_spinlock_t *lock);int pthread_spin_unlock(pthread_spinlock_t *lock);二者是「互补关系」,核心差异在「阻塞方式」和「性能开销」:
✅ 核心结论:短持有 + 低竞争 + 多核→自旋锁;其他场景→互斥锁。
✅ 选型原则:仅原子操作→自旋锁;多读少写→读写锁。
✅ 核心结论:用户态用 pthread 自旋锁,内核态用内核自旋锁。
代码才是硬道理,下面是3个示例
实现多核 CPU 下的高频计数器累加,对比「自旋锁」和「互斥锁」的性能差异:
count++原子操作,锁持有时间 < 1μs;#include <stdio.h>#include <stdlib.h>#include <pthread.h>#include <unistd.h>#include <time.h>#define THREAD_NUM 10 // 线程数#define ADD_NUM 1000000 // 每个线程累加次数// 全局共享计数器long long g_count = 0;// 自旋锁pthread_spinlock_t spinlock;// 线程函数:自旋锁保护计数器累加void* add_count(void* arg){ (void)arg; // 屏蔽未使用参数警告 for(int i=0; i<ADD_NUM; i++) { // 加锁:仅保护count++ pthread_spin_lock(&spinlock); g_count++; // 原子操作,耗时<1μs // 解锁:必须成对 pthread_spin_unlock(&spinlock); } return NULL;}int main(){ pthread_t tid[THREAD_NUM]; struct timespec start, end; double cost_time; // 检查CPU核心数(仅多核运行) int cpu_num = sysconf(_SC_NPROCESSORS_ONLN); if (cpu_num <= 1) { printf("警告:当前CPU核心数为%d,自旋锁性能差,建议改用互斥锁!\n", cpu_num); } // 初始化自旋锁(进程内私有) pthread_spin_init(&spinlock, PTHREAD_PROCESS_PRIVATE); // 记录开始时间 clock_gettime(CLOCK_MONOTONIC, &start); // 创建10个线程 for(int i=0; i<THREAD_NUM; i++) { pthread_create(&tid[i], NULL, add_count, NULL); } // 等待所有线程完成 for(int i=0; i<THREAD_NUM; i++) { pthread_join(tid[i], NULL); } // 记录结束时间 clock_gettime(CLOCK_MONOTONIC, &end); // 销毁自旋锁 pthread_spin_destroy(&spinlock); // 计算耗时(秒) cost_time = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9; printf("=====================================\n"); printf("最终计数器值:%lld(预期:%d)\n", g_count, THREAD_NUM*ADD_NUM); printf("总耗时:%.3f秒\n", cost_time); printf("每秒累加次数:%.0f次\n", (double)(THREAD_NUM*ADD_NUM)/cost_time); printf("=====================================\n"); return 0;}运行结果(4 核 CPU)

实现非阻塞自旋锁的轮询操作,避免线程永久自旋:
pthread_spin_lock持有锁,模拟短时间操作;pthread_spin_trylock非阻塞加锁,失败则轮询重试,超过最大次数则退出;#include <stdio.h>#include <stdlib.h>#include <pthread.h>#include <unistd.h>#include <errno.h>#define MAX_RETRY 10000 // 最大轮询次数// 共享状态标志int g_flag = 0;// 自旋锁pthread_spinlock_t spinlock;// 线程1:持有锁修改标志void* thread1(void* arg){ (void)arg; // 加锁 pthread_spin_lock(&spinlock); printf("线程1:获取自旋锁,修改flag为1\n"); g_flag = 1; // 模拟短时间持有锁(1ms) usleep(1000); // 解锁 pthread_spin_unlock(&spinlock); printf("线程1:释放自旋锁\n"); return NULL;}// 线程2:非阻塞轮询加锁void* thread2(void* arg){ (void)arg; int retry = 0; while(retry < MAX_RETRY) { // 非阻塞加锁 int ret = pthread_spin_trylock(&spinlock); if(ret == 0) { // 加锁成功 printf("线程2:第%d次尝试,获取自旋锁,flag=%d\n", retry+1, g_flag); g_flag = 2; printf("线程2:修改flag为2,释放自旋锁\n"); pthread_spin_unlock(&spinlock); break; } else if(ret == EBUSY) { // 锁被占用,继续轮询 retry++; // 轮询间隔(可选,降低CPU占用) // usleep(1); } else { printf("线程2:加锁失败,错误码=%d\n", ret); break; } } if(retry >= MAX_RETRY) { printf("线程2:超过最大重试次数(%d),退出\n", MAX_RETRY); } return NULL;}int main(){ pthread_t tid1, tid2; // 初始化自旋锁 pthread_spin_init(&spinlock, PTHREAD_PROCESS_PRIVATE); // 创建线程1(先持有锁) pthread_create(&tid1, NULL, thread1, NULL); usleep(100); // 确保线程1先执行 // 创建线程2(轮询加锁) pthread_create(&tid2, NULL, thread2, NULL); // 等待线程完成 pthread_join(tid1, NULL); pthread_join(tid2, NULL); // 销毁自旋锁 pthread_spin_destroy(&spinlock); printf("主线程:最终flag=%d\n", g_flag); return 0;}运行结果

实现自旋锁和互斥锁的性能对比测试,帮助快速选型:
#include <stdio.h>#include <stdlib.h>#include <pthread.h>#include <unistd.h>#include <time.h>#define THREAD_NUM 8 // 线程数#define ADD_NUM 500000 // 每个线程累加次数// 共享计数器long long g_spin_count = 0;long long g_mutex_count = 0;// 自旋锁&互斥锁pthread_spinlock_t spinlock;pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;// 自旋锁累加函数void* spin_add(void* arg){ (void)arg; for(int i=0; i<ADD_NUM; i++) { pthread_spin_lock(&spinlock); g_spin_count++; pthread_spin_unlock(&spinlock); } return NULL;}// 互斥锁累加函数void* mutex_add(void* arg){ (void)arg; for(int i=0; i<ADD_NUM; i++) { pthread_mutex_lock(&mutex); g_mutex_count++; pthread_mutex_unlock(&mutex); } return NULL;}// 耗时计算函数double calc_cost(struct timespec start, struct timespec end){ return (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;}int main(){ pthread_t tid[THREAD_NUM]; struct timespec start, end; double spin_cost, mutex_cost; // 检查CPU核心数 int cpu_num = sysconf(_SC_NPROCESSORS_ONLN); printf("当前CPU核心数:%d\n", cpu_num); // ========== 测试自旋锁性能 ========== pthread_spin_init(&spinlock, PTHREAD_PROCESS_PRIVATE); clock_gettime(CLOCK_MONOTONIC, &start); for(int i=0; i<THREAD_NUM; i++) { pthread_create(&tid[i], NULL, spin_add, NULL); } for(int i=0; i<THREAD_NUM; i++) { pthread_join(tid[i], NULL); } clock_gettime(CLOCK_MONOTONIC, &end); spin_cost = calc_cost(start, end); pthread_spin_destroy(&spinlock); // ========== 测试互斥锁性能 ========== clock_gettime(CLOCK_MONOTONIC, &start); for(int i=0; i<THREAD_NUM; i++) { pthread_create(&tid[i], NULL, mutex_add, NULL); } for(int i=0; i<THREAD_NUM; i++) { pthread_join(tid[i], NULL); } clock_gettime(CLOCK_MONOTONIC, &end); mutex_cost = calc_cost(start, end); pthread_mutex_destroy(&mutex); // ========== 输出结果 ========== printf("=====================================\n"); printf("自旋锁测试结果:\n"); printf(" 最终计数:%lld\n", g_spin_count); printf(" 总耗时:%.3f秒\n", spin_cost); printf(" 每秒操作:%.0f次\n", (double)(THREAD_NUM*ADD_NUM)/spin_cost); printf("互斥锁测试结果:\n"); printf(" 最终计数:%lld\n", g_mutex_count); printf(" 总耗时:%.3f秒\n", mutex_cost); printf(" 每秒操作:%.0f次\n", (double)(THREAD_NUM*ADD_NUM)/mutex_cost); printf("性能提升:%.1f倍\n", mutex_cost/spin_cost); printf("=====================================\n"); return 0;}运行结果(4 核 CPU)

timedlock接口,无法设置自旋超时,易导致永久忙等;全文核心总结