在多线程编程中,同步时会发生ABA问题。上述代码就是重现ABA问题,当一个地址需要被read2次,这个值没有改变意味着没有人修改这个值。然而其他线程执行可能会修改这个值在两次read之间,只是又把这个值修改回去了。
- 线程P1读取值A从共享内存中
- P1运行后,P2在运行。
- P2 修改存储A的共享内存修改为B,然后在修改回A
- P1继续执行,发现存储A的共享内存处的值并没有变化,于是继续运行
这个可能会造成一些隐藏的问题在修改这块共享内存之后。
最经常会出现ABA问题的一个情况,就是编写lock-free程序时,包含指针的情况。如果一个item从列表中移出以及释放掉,接着一个新的item被分配以及加入列表。新分配的item和之前释放掉的item指针相同这个经常会发生的(基于内存分配器的分配策略)。这个就导致了ABA问题了。
简书中链接跳转到github一直提示不安全的链接,所以把链接贴进来。https://github.com/iamjokerjun/abaTest。
这个是个ABA问题的栈操作。列举下步骤
- 初始化栈 top->A->B->C
Push(&C);
Push(&B);
Push(&A);
- thread1的worker1先运行,thread2阻塞到等待信号量。thread1中的局部变量retPtr指向A,nextPtr指向B,接着post信号量,然后sleep 2秒,有足够的时间让thread2 执行完代码
for(;;){
struct Obj* retPtr = topPtr;
if(!retPtr)
return NULL;
struct Obj* nextPtr = retPtr->next;
//第一步 retPtr指向A nextPtr指向B
if(bSleep)
{
sem_post(&Sema); //post信号量
sleep(2);
fprintf(stdout,"i am wake up topPtr.value[%c] retPtr.value[%c] nextPtr.value[%c]\n",((struct Obj*)topPtr)->value,retPtr->value,nextPtr->value);
}
if(topPtr.compare_exchange_weak(retPtr,nextPtr))
return retPtr;
}
- thread2的worker2开始运行了,最后结果为top->A->C.注意这里A我们是全局的变量,不会被释放的。这里就等于模拟了,释放了A后,在分配一块内存,内存地址和A是一样的。
Pop(false); //top -> B -> C
Pop(false); //top -> C
Push(&A); //top ->A -> C
- thread1的worker1开始苏醒过来了。执行这段代码,判断topPtr和retPtr地址都相同,所以就用nextPtr替换topPtr,注意到步骤1中,nextPtr指向B的,要知道B在步骤3中第一个Pop中已经被释放了。
if(topPtr.compare_exchange_weak(retPtr,nextPtr))
return retPtr;
运行的结果如下
Obj A malloc
Obj B malloc
Obj C malloc
worker1 is work
worker2 is wake
worker2 is over
i am wake up topPtr.value[A] retPtr.value[A] nextPtr.value[B]
worker1 is over
Obj C free
Obj B free
Segmentation fault
为了保证每次必定crash,我在调用之后把栈上数据清0,所以一定会崩溃。
testFun();
char array[64] = {0,}; //栈上之前分配的B,C虽然已经释放掉了,但是栈上数据还存在。所以把栈数据清0,让其必定崩溃。
struct Obj* APtr = topPtr;
网友评论