我想在线程找到一个新的最小值来更新atomicX时更新atomicX.当它设置新的最小值时,我还想以原子方式更改变量y.有没有办法在没有锁的情况下做到这一点?
一次在多个线程上执行的线程函数示例:
uint64_t x = atomicX; int y = g(); for(int newX = 0; newX < x; ++newX) { if(f(newX)) { while(newX < x && !atomicX.compare_exchange_strong(x,newX)); // also set atomicY to y if the exchange happened above break; } x = atomicX; }
我可以用锁来做到这一点:
int y = g(); for(uint64_t newX = 0; newX < atomicX; ++newX) { if(f(newX)) { mutex.lock(); if(newX < atomicX) { atomicX = newX; atomicY = y; // atomicY no longer needs to be atomic } mutex.unlock() break; } }
我也愿意接受任何更清洁的结构,或者另外一种方式来完成这一切.我不喜欢我必须拥有相同的newX< x条件两次,或者我必须打破循环.
解决方法
有一个相当简单且可能足够便携的解决方案,即使用指针和CAS:
struct XY { uint64_t x; uint32_t y; }; std::atomic<XY *> globalXY;
然后,棘手的一点变得弄清楚如何在没有过多成本或ABA问题的情况下分配和释放这些对象.
为清楚起见,代码最终会是这样的:
XY *newXY = somehow_allocate_objects(); XY *oldXY = globalXY; int oldX = oldXY->x; newXY->y = g(); for(int newX = 0; newX < oldX; ++newX) { if(f(newX)) { // prepare newXY before swapping newXY->x = newX; while(newX < oldX && !globalXY.compare_exchange_strong(oldXY,newXY)) { // oldXY was updated,reload oldX oldX = oldXY->x; } // globalXY->x,y both updated by pointer CAS break; } oldXY = globalXY; oldX = oldXY->x; }
作为参考,最终的结论是这些线程是长寿的,因此为每个线程静态分配单个XY实例就足够了.