Skip to content

自旋锁

实现方式:

  • TAS
  • CAS

TAS

原理:

向内存变量写入1,然后返回内存变量的原值

伪代码

cpp
// Test And Set
int TAS(int *lock)
{
    int temp = *lock; // 读取旧值
    *lock = 1;        // 无条件设置为1
    return temp;      // 返回旧值
}

void acquire_lock(int *lock)
{
    while (TAS(lock) == 1)
    {
        continue;
    }
}

void release_lock(int *lock)
{
    *lock = 0;
}

CAS

原理:

比较锁种的值和期望值

  • 如果锁中的值和期望值相同,则设置为新值,返回true
  • 否则不设置新值,返回false

伪代码

cpp
// Compare And Swap
bool CAS(int *lock, int expected, int value)
{
    if (*lock == expected)
    {
        *lock = value;
        return true;
    }
    else
    {
        return false;
    }
}

void acquire_lock(int *lock)
{
    while (!CAS(lock, 0, 1))
    {
        continue;
    }
}

void release_lock(int *lock)
{
    *lock = 0;
}

示例

cpp
#include "stdbool.h"
#include "stdatomic.h"
#include "stdio.h"
#include "pthread.h"
#include "unistd.h"

typedef struct
{
    atomic_int lock;
} spinlock_t;

void acquire_lock(spinlock_t *lock)
{
    int expected = 0;
    while (!atomic_compare_exchange_weak_explicit(
        &lock->lock,
        &expected,
        1,
        memory_order_acquire,
        memory_order_relaxed))
    {
        expected = 0;
        // 减少忙等待
    }
}

void release_lock(spinlock_t *lock)
{
    atomic_store_explicit(&lock->lock, 0, memory_order_release);
}

int count = 0;
spinlock_t lock = {0};

void *run_task(void *arg)
{
    for (size_t i = 0; i < 10000; i++)
    {
        acquire_lock(&lock);
        count++;
        release_lock(&lock);
    }
    return NULL;
}

int main(int argc, char const *argv[])
{
    const int nthread = 100;
    pthread_t t[nthread];
    for (int i = 0; i < nthread; i++)
    {
        int ret = pthread_create(&t[i], NULL, run_task, NULL);
        if (ret == 0)
        {
            printf("create thread success\n");
        }
        else
        {
            printf("create error, ret: %d\n", ret);
        }
    }

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

    printf("count: %d\n", count);

    return 0;
}

区别

  • TAS:不管原来是什么,都写1
  • CAS: 只有原来是0,才会写1

TTAS

优化 test and test and set

先读取,发现锁空闲再进行CAS

cpp
void acquire_lock(int *lock)
{
    for(;;){
        while(atomic_load_explicit(lock, memory_order_relaxed) !=0 ){
            continue;
        }

        if (!CAS(lock, 0, 1))
        {
            return;
        }
    }
}