比较和交换功能可以用于原子交换变量吗? 我在x86_64 RedHat Linux上通过gcc使用C / C ++,特别是__sync builtins。 例如:
int x = 0, y = 1;
y = __sync_val_compare_and_swap(&x, x, y);
我认为这归结为x是否可以在& x和x之间变换;例如,如果& x构成一个操作,则x可能在参数中的& x和x之间变化。我想假设上面隐含的比较总是正确的;我的问题是我是否可以。显然有CAS的bool版本,但是我不能让旧的x写入y。
一个更有用的例子可能是从链接列表的头部插入或删除(gcc声称支持指针类型,所以假设这是元素和头部):
elem->next = __sync_val_compare_and_swap(&head, head, elem); //always inserts?
elem = __sync_val_compare_and_swap(&head, head, elem->next); //always removes?
答案 0 :(得分:3)
该操作实际上可能不会将新值存储到目标中,因为与另一个线程的竞争会在您尝试的同一时刻更改该值。 CAS原语不保证写入发生 - 只有当值已经是预期值时才发生写入。如果值不是预期值,则原语无法知道正确的行为是什么,因此在这种情况下没有任何反应 - 您需要通过检查返回值来确定操作是否有效来解决问题。
所以,你的例子:
elem->next = __sync_val_compare_and_swap(&head, head, elem); //always inserts?
不一定会插入新元素。如果另一个线程同时插入一个元素,那么可能会导致该线程对__sync_val_compare_and_swap()
的调用不更新head
的竞争条件(但是如果你这个线程或其他线程的元素都没有丢失)正确处理它)。
但是,这行代码存在另一个问题 - 即使head
确实得到更新,head
指向插入元素的时间很短,但该元素的{{1} }指针尚未更新为指向列表的上一个头部。如果另一个线程在那一刻猛扑进去并试图走在列表中,就会发生不好的事情。
要正确更新列表,请将该行代码更改为:
next
或者使用whatever_t* prev_head = NULL;
do {
elem->next = head; // set up `elem->head` so the list will still be linked
// correctly the instant the element is inserted
prev_head = __sync_val_compare_and_swap(&head, elem->next, elem);
} while (prev_head != elem->next);
变体,我认为这样更方便:
bool
这有点难看,我希望我做对了(在线程安全代码的细节中很容易被绊倒)。它应该包含在do {
elem->next = head; // set up `elem->head` so the list will still be linked
// correctly the instant the element is inserted
} while (!__sync_bool_compare_and_swap(&head, elem->next, elem));
函数中(或者更好,使用适当的库)。
解决ABA问题:
我不认为ABA问题与“将元素添加到列表头部”代码相关。假设一个线程想要将对象insert_element()
添加到列表中,并且当它执行X
时,elem->next = head
具有值head
。
然后在执行A1
之前,又出现了另一组线程:
__sync_val_compare_and_swap()
,使A1
指向head
B
执行任何操作并释放它A1
碰巧与A2
所在的地址相同A1
添加到列表中,以便A2
现在指向head
由于A2
和A1
具有相同的标识符/地址,因此这是ABA问题的一个实例。
但是,在这种情况下并不重要,因为线程添加对象A2
并不关心X
指向的是与其开始时不同的对象 - 所有它关心的是当head
排队时:
X
以外的任何对象(通过此主题)答案 1 :(得分:0)
不。 x86上的CAS指令从寄存器中获取值,并将其与内存中的值进行比较/写入。
为了原子地交换两个变量,它必须使用两个内存操作数。
至于x
和&x
之间x
是否可以更改?是的,当然可以。
即使没有&
,也可能会发生变化。
即使在诸如Foo(x, x)
之类的函数中,也可以得到两个不同的x值,因为为了调用该函数,编译器必须:
在这两个操作之间,另一个线程可以轻松修改x
。
答案 2 :(得分:0)
看起来你正在寻找的是互锁交换原语,而不是互锁比较交换。这将无条件地将保持寄存器与目标存储器位置原子交换。
但是,对y
的作业之间的竞争条件仍有问题。有时y
是本地的,在这种情况下这是安全的,但如果共享x
和y
,则会遇到重大问题,需要锁定才能解决问题。