在并发运行时环境中,简单的事情看起来很奇怪……希望这可以有所帮助。
我们有一个内置原子CAS1,其语义如下:
int CAS1(int *addr, int oldval, int newval) {
int currval = *addr;
if (currval == oldval) *addr = newval;
return currval;
}
我们需要定义一个原子RDCSS功能使用CAS1和具有以下语义:
int RDCSS(int *addr1, int oldval1, int *addr2, int oldval2, int newval2) {
int res = *addr;
if (res == oldval2 && *addr1 == oldval1) *addr2 = newval2;
return res;
}
直观地说:仅当* addr1 == oldval1 ...时,我们才需要同时更改addr2的值。如果另一个线程正在更改它,我们可以帮助另一个线程完成操作,然后可以重试。
将使用RDCSS函数(请参阅文章)来定义CASN。现在,我们通过以下方式定义RDCSS描述符:
RDCSSDESCRI
int *addr1
int oldval1
int *addr2
int oldval2
int newval2
然后,我们通过以下方式实现RDCSS:
int RDCSS( RDCSSDESCRI *d ) {
do {
res = CAS1(d->addr2, d->oldval2, d); // STEP1
if (IsDescriptor(res)) Complete(res); // STEP2
} while (IsDescriptor(res); // STEP3
if (res == d->oldval2) Complete(d); // STEP4
return res;
}
void Complete( RDCSSDESCRI *d ) {
int val = *(d->addr1);
if (val == d->oldval1) CAS1(d->addr2, d, d->newval2);
else CAS1(d->addr2, d, d->oldval2);
}
- 步骤1:首先,我们尝试将* addr2的值更改为(自己的)描述符d,如果CAS1成功,则res == d-> oldval2(即res不是描述符)
- STEP2:检查res是否为描述符,即STEP1失败(另一个线程更改了addr2)...帮助另一个线程完成操作
- 第3步:如果未能成功存储描述符d,请重试第1步
- 第4步:如果我们从addr2获取了期望值,那么我们就成功地将描述符(指针)存储在addr2中,并且可以完成将newval2存储到* addr2 iif * addr1 == oldval1的任务
回答您的问题
如果我们省略了STEP4,则将永远不会执行RDCSS语义的if(... && * addr1 == oldval1)* addr2 = newval2部分(...或更好:它可以由其他线程以可预测的方式执行)当前的)。
正如您在评论中所指出的那样,在STEP4上不需要if(res == d1-> oldval2)条件:即使我们忽略它,Complete()中的两个CAS1也会失败,因为*(d-> addr2)!= d 。其唯一目的是避免函数调用。
示例T1 =线程1,T2 =线程2:
remember that addr1 / addr2 are in a shared data zone !!!
T1 enter RDCSS function
T2 enter RDCSS function
T2 complete STEP1 (and store the pointer to its descriptor d2 in addr2)
T1 at STEP1 the CAS1 fails and res = d2
T2 or T1 completes *(d2->addr2)=d2->newval2 (suppose that *(d2->addr1)==d2->oldval1)
T1 execute STEP1 and now CAS1 can fail because *addr2 == d2->newval2
and maybe d2->newval2 != d1->oldval2, in every case at the end
res == d2->newval2 (fail) or
res == d1->oldval2 (success)
T1 at STEP2 skips the call to Complete() (because now res is not a descriptor)
T1 at STEP3 exits the loop (because now res is not a descriptor)
T1 at STEP4 T1 is ready to store d1->newval2 to addr2, but only if
*(d1->addr2)==d (we are working on our descriptor) and *(d1->addr1)==d1->oldval1
( Custom() function)