C 交换指针竞争条件

C swap Pointer Race Conditions

我目前正在开发一个 C 程序,它或多或少是一个统一的(解)多路复用器,用户可以在其中创建输入和输出之间的映射。虽然目的地只保留一次并且可用于所有源,但一个源可以在不同组中存在一次或多次(但不能在同一组中出现两次)

无论如何这是我的背景。为了使群组有用,我实现了一个根据用户输入切换群组的功能。

当前活动组由指针持有"activeGroup"。

每个输出目标都有一个线程等待输入并检查活动组的映射,因此使用 "activeGroup" 指针。

我今天的测试环境使用了ca。 2kHz 输入,而最终用户输入可能更高(例如 1000kHz,考虑到非活动源也可以发送数据)。没有明显的问题。

我现在的问题:

我应该为 "activeGroup" 指针使用 RWLock 吗?原子地简单交换指针是一个问题吗?一般来说,我不介意一个或一百个输入包在短短的几分之一秒内到达错误的目的地,但如果读取线程在绝对错误的时刻读取,我会不会以某种方式出现分段错误?

唯一写入指针的函数:

bool activate_group(char *groupName){
    ... (get correct group to activate)
    if(newActive){
        if(activeGroup)
            activeGroup->isActive = false;
        // the only writing line to activegroup
        activeGroup = grp;
        grp->isActive = true;
        return true;
    }
    return false;
}

访问它的两个函数之一(非常相似)

void forward_sender(...){

    if(activeGroup && (input = FINDCORRECTINPUTSOUCEIN(activeGroup->mappingList)){

        snd_params par;
        par.sockfd = sockfd;
        par.msg = buffer;
        par.len = len;
        sendMultipleByInput(activeGroup->mappingList, sender, &par);
    }
}

另一个函数直接发送,这个可以发送多次。

如果 activeGroup 在两者之间切换,sendMultipleByInput 将不会发送,因为它不会在现在新的活动映射列表中找到设备之间的映射。

如果当前实现可能发生这种情况,我是否可以通过简单地访问 activeGroup 指针一次并将值复制到本地指针来避免它?

我猜,如果有的话,那会把可能性降到最低?

由于性能是我程序的关键因素,我喜欢尽可能多的同步方法,这些方法可能会减慢速度甚至导致死锁(不太可能在这里使用 rwlock 更喜欢 writer,但仍然)

感谢任何见解

到目前为止还没有人回答,所以希望我能提供帮助。

要扩展死锁注释,请考虑经典示例...

T1: A -> B
T2: B -> A

你可以避免死锁(T1T2 都可以取得进展)只需将 T2 的顺序切换为...

T2: A -> B

绝对不应该为了避免死锁而避免锁。不过,使用 rwlock 你的直觉是正确的,所以最好还是尽量减少关键部分的范围。

锁的性能不应该是一个问题。大多数锁实现将避免进入内核。如果延迟真的是一个问题,那么有很多好的自旋锁实现。

不使用显式同步几乎肯定会导致不正确的行为。将指针写入内存 在大多数体系结构(尤其是 x86)上通常是一个原子操作,但未定义读写在 cores/processors 中发生的顺序。

例如,在您的代码中...

bool activate_group(char *groupName){
    ... (get correct group to activate)
    if(newActive){
        if(activeGroup)
            activeGroup->isActive = false;
        // the only writing line to activegroup
        activeGroup = grp;
        grp->isActive = true;
        return true;
    }
    return false;
}

和...

void forward_sender(...){

    if(activeGroup && (input = FINDCORRECTINPUTSOUCEIN(activeGroup->mappingList)){

        snd_params par;
        par.sockfd = sockfd;
        par.msg = buffer;
        par.len = len;
        sendMultipleByInput(activeGroup->mappingList, sender, &par);
    }
}

有许多不同的有效内存排序。特别是,没有什么可以保护 mappingListactiveGroup 之前不被更新。实际顺序几乎与程序顺序无关。更重要的是,编译器总是会避免内存读写。所以这些值更有可能留在寄存器中,甚至不会接触内存。

!!!危险地带!!!

如果无锁编程是绝对必要的 C11 添加了无锁atomics。但是,除非您完全理解各种内存排序排列,否则您几乎肯定会得到意想不到的行为。