Linux 自旋锁的使用

自旋锁(spinlock)在Linux中用于在多线程环境下保护共享资源。自旋锁在等待锁时会一直循环,适用于短时间持有锁的场景。互斥锁(mutex)是最常用的锁,当一个线程请求该锁时,如果锁已被占用,线程会被阻塞直到锁被释放。互斥锁与自旋锁不同的是,互斥锁竞争时会导致线程上下文切换,而自旋锁会一直占用CPU。当等待的开销小于上下文切换的开销时,我们可以选择使用自旋锁。
示例:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>

pthread_spinlock_t spinlock;
pthread_mutex_t mutex;
int counter = 0;

void* increment(void* arg) {
    for (int i = 0; i < 100000; ++i) {
        pthread_spin_lock(&spinlock);
        counter++;
        pthread_spin_unlock(&spinlock);
    }
    return NULL;
}

void* increment2(void* arg) {
    for (int i = 0; i < 100000; ++i) {
        pthread_mutex_lock(&mutex);
        counter++;
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

int main() {
    struct timeval start, end;  // 统计时间
    int timeuse;
    
    pthread_t t1, t2;
    pthread_spin_init(&spinlock, PTHREAD_PROCESS_PRIVATE);
    gettimeofday(&start, NULL);
    pthread_create(&t1, NULL, increment, NULL);
    pthread_create(&t2, NULL, increment, NULL);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    gettimeofday(&end, NULL);
    pthread_spin_destroy(&spinlock);
    timeuse = 1000000 * ( end.tv_sec - start.tv_sec ) + end.tv_usec - start.tv_usec; 
    printf("spinlock use time: %d us\n", timeuse);

    pthread_t t3, t4;
    pthread_mutex_init (&mutex,NULL);
    gettimeofday(&start, NULL);
    pthread_create(&t3, NULL, increment2, NULL);
    pthread_create(&t4, NULL, increment2, NULL);
    pthread_join(t3, NULL);
    pthread_join(t4, NULL);
    gettimeofday(&end, NULL);
    pthread_mutex_destroy(&mutex);
    timeuse = 1000000 * ( end.tv_sec - start.tv_sec ) + end.tv_usec - start.tv_usec; 
    printf("mutex use time: %d us\n", timeuse);

    return 0;
}

编译运行我们可以得到类似如下的结果:

# ./a.out
spinlock use time: 7367 us
mutex use time: 16383 us

如此可以得出,当锁里的开销较小时使用自旋锁能提高效率。