You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用每个条目仅4位实现双向表线程安全且无全局锁?

线程安全双射表锁机制设计问题

场景描述

我在设计安全高效的锁机制时遇到了困难,以下是简化后的核心场景:

  • 有两个表A和B,各含10个条目:其中9个是指向另一表的指针,剩余1个为NULL
  • 程序初始化完成后,非NULL条目构成双射关系(即从A的某条目指向B,再从B的对应条目返回A时,会回到原位置,反之亦然),同时用全局变量跟踪两个表中NULL的位置
  • 启动两个工作线程:
    • Thread A:随机选择A中的一个非NULL条目,与A中的NULL位置交换,同时调整B中对应的指针以维持双射关系
    • Thread B:对表B执行完全相同的操作

核心问题

如何在不锁定整个表的前提下实现该系统的线程安全?优先考虑利用每个指针的低4位来实现锁机制。

实际场景中表的规模远大于10,每个条目附带少量额外数据,且移动操作并非完全随机。

补充:三个难度版本的需求

我需要针对以下三个版本分别提供解决方案:

  • 简易版:如果选中的条目无法移动,可随机选择其他条目重试
  • 中等版:仅允许表A放弃重试;表B必须阻塞等待,直到选中的条目可以移动
  • 困难版:两个表的操作均不允许放弃重试,必须阻塞等待直到操作可执行

补充说明2:无锁示例代码(x86-64/Linux)

当前代码设计使用指针的低3位存储额外标记,若需要可升级为128位条目以使用低4位:

#include <pthread.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

// 清除64位值的低3位,获取原始指针
#define GET_POINTER(val) (uint64_t*) ((val) & ~0x7ULL)

// 将新指针的高61位与旧值的低3位拼接(要求新指针的低3位为0)
#define MODIFY_VALUE(new_ptr, old_val) ((uint64_t) (new_ptr)) ^ ((old_val) & 0x7ULL) 

// 全局变量声明
uint64_t A[10], B[10];
int index_of_NULL_value_A, index_of_NULL_value_B;

// 初始化表结构
void init_globals() {
    // 初始化A[0]和B[0]为NULL指针
    A[0] = (uint64_t) NULL; // (uint64_t) NULL == 0
    B[0] = (uint64_t) NULL;

    // 记录初始的NULL位置索引
    index_of_NULL_value_A = 0;
    index_of_NULL_value_B = 0;

    // 创建A到B的指针映射
    for (int i = 1; i < 10; ++i) {
        A[i] = (uint64_t) &B[i];
    }

    // 创建B到A的指针映射
    for (int i = 1; i < 10; ++i) {
        B[i] = (uint64_t) &A[i];
    }
}

// 验证单个表的完整性
void verify_integrity_of_table(uint64_t* table, int null_index) {
    for (int i = 0; i < 10; ++i) {
        if (i == null_index) {
            // 检查NULL位置是否正确
            if (table[i] != (uint64_t) NULL) {
                fprintf(stderr, "表完整性检查失败!位置%d未找到NULL\n", i);
                exit(1);
            }
        } else {
            // 检查双射关系是否成立
            if (&table[i] != GET_POINTER(*GET_POINTER(table[i]))) {
                fprintf(stderr, "表完整性检查失败!位置%d的链接无效\n", i);
                exit(1);
            }
        }
    }
}

// 验证整个系统的完整性
void verify_integrity() {
    verify_integrity_of_table(A, index_of_NULL_value_A);
    verify_integrity_of_table(B, index_of_NULL_value_B);
}

// 创建线程,失败则退出
typedef void *(*start_routine_t)(void *);
pthread_t pthread_create_or_exit(start_routine_t start_routine) {
    pthread_t thread_id;
    int result = pthread_create(&thread_id, NULL, start_routine, NULL);
    if (result != 0) {
        perror("创建线程失败!");
        exit(EXIT_FAILURE);
    }
    return thread_id;
}

// 执行一次随机交换操作
void do_a_random_swap(uint64_t* table, int* null_index_ptr) {
    // 获取当前NULL的位置
    int null_index = *null_index_ptr;

    // 随机选择一个非NULL的条目
    int target_idx = rand() % 10;
    while (target_idx == null_index) {
        target_idx = rand() % 10;
    }

    // 更新反向指针
    uint64_t* new_back_ptr = &table[null_index];
    uint64_t old_back_val = *(GET_POINTER(table[target_idx]));
    *GET_POINTER(table[target_idx]) = MODIFY_VALUE(new_back_ptr, old_back_val);

    // 交换NULL位置和目标条目
    table[null_index] = table[target_idx];
    table[target_idx] = (uint64_t) NULL;

    // 更新NULL位置的跟踪变量
    *null_index_ptr = target_idx;
}

// 线程A的执行函数:操作表A
void* fst_start_routine(void* arg) {
    (void) arg; // 忽略未使用的参数
    while (1) {
        if (time(NULL) % 2 == 0) {
            do_a_random_swap(A, &index_of_NULL_value_A);
        } else {
            usleep(100000); // 睡眠0.1秒
        }
    }
    return NULL;
}

// 线程B的执行函数:操作表B
void* snd_start_routine(void* arg) {
    (void) arg; // 忽略未使用的参数
    while (1) {
        if (time(NULL) % 2 == 0) {
            do_a_random_swap(B, &index_of_NULL_value_B);
        } else {
            usleep(100000); // 睡眠0.1秒
        }
    }
    return NULL;
}

// 完整性检查线程的执行函数
void* integrity_checker_start_routine(void* arg) {
    (void) arg; // 忽略未使用的参数
    for (;; usleep(100000)) {
        if (time(NULL) % 2 == 1) {
            verify_integrity();
        }
    }
    return NULL;
}

int main() {
    // 初始化随机数种子
    srand(time(NULL));

    // 初始化表和NULL位置跟踪变量
    init_globals();

    // 验证初始状态的完整性
    verify_integrity();

    // 创建工作线程和检查线程
    pthread_create_or_exit(fst_start_routine);
    pthread_create_or_exit(snd_start_routine);
    pthread_create_or_exit(integrity_checker_start_routine);

    // 主线程永久睡眠
    while (1) {
        sleep(1);
    }
    return 0;
}

内容的提问来源于stack exchange,提问作者SocraticMathTutor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 08:22:48