问题描述
以下伪代码摘自http://15418.courses.cs.cmu.edu/spring2013/article/46
while (1) {
n->next = p->next;
Node *old_next = p->next;
if (compare_and_swap(&p->next,old_next,n) == old_next)
return;
}
这是无锁堆栈的push
操作,该操作使用比较和交换惯用法,但自动进行。似乎ABA问题在这里并不重要,我想知道推送和插入操作通常是否如此?
解决方法
您是正确的,该函数不会遇到ABA问题,因为在调用old_next
之前,没有任何内容取决于compare_and_swap
的值。
考虑(简化的)弹出操作:
while (1) {
Node *top = s->top;
if (top == NULL)
return NULL;
Node *new_top = top->next;
if (compare_and_swap(&s->top,top,new_top);
return top;
}
}
这里,我们将s->top
加载到top
中,然后在s->top
上以top
作为期望值执行CAS。但是在CAS之前,我们读了top->next
,所以我们执行的操作取决于top
!这就是导致ABA问题的原因。
通常不可能声明所有推入/插入操作都没有ABA问题,因为这取决于更多细节。作为一个有些人为的示例,请考虑仅在该值更大时才有条件地推送该值的推送操作。
while (1) {
n->next = p->next;
Node *old_next = p->next;
if (n->value < old_next->value)
return;
if (compare_and_swap(&p->next,old_next,n) == old_next)
return;
}
这也遭受ABA问题的困扰,因为它可能破坏我们对值进行严格排序的不变性。
,此代码没有ABA问题,但这确实是因为它执行的不是很多。
从根本上讲,问题是您使用compare_and_swap(&p->next,n)
来确保自从您上次查看堆栈以来,堆栈没有发生变化,但是它确实做到了( )可靠。
在您读n->next
和您读compare_and_swap
的时间之间,其他线程可能具有:
- 弹出
n->next
- 推了很多其他东西
- 重新按旧的
n->next
那么即使堆栈已更改,您的compare_and_swap
也会成功。
这对您来说不是问题,因为您从未看过n->next
的任何字段。堆栈已更改没关系,因为您只关心n->next
指针。
但是,如果您必须必须查看该对象的内部,则您的代码将被破坏。例如,如果您添加了一个stack_size
字段以原子方式跟踪堆栈大小,那么您将设置n->stack_size = old_next->stack_size+1
,如果堆栈已更改,这将是错误如上所述。
因此,插入操作是ABA防护的,通常不是 。