有没有办法分配一个指针,并原子地增加一个计数器,没有锁?

时间:2014-07-15 01:59:14

标签: c++ concurrency atomic

以这些为例

struct MyObj {
    void * x;
    int counter;
};

std::atomic<MyObj *> globalSourceThatGetsChangedByOtherThreads = ...;

这就是我想做的事情:

void someCode() {

  //begin atomic
  MyObj *dest = globalSourceThatGetsChangedByOtherThreads.load();
  globalSourceThatGetsChangedByOtherThreads->counter++;
  //end atomic
}

我试图通过x86汇编程序调用很聪明,但那不起作用。

2 个答案:

答案 0 :(得分:1)

我不知道如何使用c11原子,但我认为你的意思是这样的。我一直使用联合来使用原子比较和交换来更改多个字段。使用指针时的另一种技术是利用操作系统实际使用的地址空间。 Windows 64位仅使用44位地址,并且是8字节对齐,这意味着您只需要41位来保存指针。这为计数器和其他字段释放了23位。这里我假设64位寻址,但您只需要48位地址空间。这为计数器释放了16位:

typedef unsigned int64 QWORD;
union { 
  // This struct is what we actually work with
  struct { 
    QWORD m_x : 48,  // most os don't use the full 64 bit address space...
          m_c : 16;
  };
  // This is what we compare and swap.
  QWORD   m_n64;  
  // constructor to make the code clean.  This doesn't use memory barriers
  // so depending on your architecture, you might need a barrier.  With x64 and 
  // windows the barrier on the compare and swap has always been sufficient.
  MyObj(const volatile MyObj &r) volatile { m_n64 = r.m_n64; }
} MyObj;


while (1) {
  // its not clear to me if pOld will change or the data at pOld or both, so i
  // put volatile on both
  volatile MyObj* volatile pOld = globalSourceThatGetsChangedByOtherThreads.load();
  MyObj Old(*pOld), New(Old);
  // set lots of fields!
  New.m_x = (QWORD)somepointer;
  New.m_c++;
  // assume CompareAndSwap64 returns true if the 64 bit number was successfully swapped 
  if (CompareAndSwap64(pOld, &Old, &New))
    break;
  // if we get here, rare, we had a race.  Try again.
}

答案 1 :(得分:0)

我认为这会奏效。

 struct MyObj {
        void * x;
        std::atomic_int counter;
 };

 std::atomic<MyObj *> globalSourceThatGetsChangedByOtherThreads = ...;


void someCode() {
  MyObj *dest;
  while (true) {
    dest = globalSourceThatGetsChangedByOtherThreads.load();
    //globalSourceThatGetsChangedByOtherThreads could have changed between above and below
    dest->counter++;
    // check
    if (dest == globalSourceThatGetsChangedByOtherThreads.load())
        break;
    else
        dest->counter--;
  }
  dest->x->doSomethingReadOnly();
  dest->counter--;
}
相关问题