Skip to content

自旋锁

锁实现的困难点:不能同时读/写共享内存

  • load (环顾四周) 的时候不能写,只能看一眼,看到的东西立马就过时了
  • store (改变状态) 的时候不能读,只能盲改,也不知道把啥呢么改成了什么

项目结构

shell
main.c
spin_lock.c
spin_lock.h

代码实现

spin_lock.h

cpp
typedef int spin_lock_t;

void spin_lock(spin_lock_t *lock);

void spin_unlock(spin_lock_t *lock);

spin_lock.c

cpp
// spin_lock.c
#include "spin_lock.h"

static inline int atomic_xchg(volatile int *addr, int newval)
{
    int result;
    asm volatile("lock xchg %0, %1" : "+m"(*addr), "=a"(result) : "1"(newval) : "memory");
    return result;
}

void spin_lock(spin_lock_t *lock)
{
    while (1)
    {
        if (atomic_xchg(lock, 1) == 0)
        {
            break;
        }
    }
}

void spin_unlock(spin_lock_t *lock)
{
    atomic_xchg(lock, 0);
}

main.c

cpp
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include "spin_lock.h"

#define N 1000000

spin_lock_t lock;
long n, sum = 0;

void *get_sum(void *arg)
{   
    for (int i = 0; i < n; i++)
    {
        spin_lock(&lock);
        sum++;
        spin_unlock(&lock);
    }

    return NULL;
}

int main(int argc, char const *argv[])
{
    printf("argc: %d\n", argc);
    if (argc != 2){
        return 1;
    }

    int nthread = atoi(argv[1]);
    n = N / nthread;
    pthread_t t[nthread];
    for (int i = 0; i < nthread; i++)
    {
        pthread_create(&t[i], NULL, get_sum, NULL);
    }

    for (int i = 0; i < nthread; i++)
    {
        pthread_join(t[i], NULL);
    }

    printf("sum: %ld\n", sum);
    return 0;
}

输出结果

shell
gcc -g spin_lock.c main.c -o main -lpthread && time ./main 100

统计结果

nrealusersys
10m0.032s0m0.030s0m0.001s
100m0.446s0m3.379s0m0.014s
1000m2.983s0m23.499s0m0.167s
10000m0.236s0m0.049s0m0.470s
100000m2.196s0m0.185s0m4.123s

结论:自旋锁的存在,线程数越多,耗时越久

使用自旋锁的场景:

  1. 临界区同一时间只有一个线程进入
  2. 操作系统内核的并发数据结构(短临界区)

对比:pthread_mutex_t

nrealusersys
10m0.031s0m0.027s0m0.002s
100m0.083s0m0.151s0m0.240s
1000m0.095s0m0.192s0m0.382s
10000m0.241s0m0.043s0m0.469s
100000m2.427s0m0.205s0m4.609s