在多线程环境中用原子保护两个变量

protect two variables with atomic in a multi-threading environement

//线程1

std::uint64_t getAndResetProcessingTimeInMicro()
{
    auto value = m_cumulatedProcessingTimeCount.load(boost::memory_order_acquire);

    if (value == 0)
        return 0;
    auto processingTime = m_cumulatedProcessingTime.load(boost::memory_order_relaxed);

    resetProcessingTimeInMicro();

    return processingTime / value;
}

//线程2

void addProcessingTimeInMicro(std::uint64_t processedTime)
{ 
    m_cumulatedProcessingTime.fetch_add(processedTime, boost::memory_order_relaxed); 
    //ctxt switch here  //-----HERE------
    m_cumulatedProcessingTimeCount.fetch_add(1, boost::memory_order_release);
}

线程 1 计算平均处理时间,线程 2 累积处理时间。

我想确保上下文切换不会在不引入锁的情况下破坏 HERE 位置的数据,我可以通过使用原子对象来实现这个结果吗?

如果您想一起执行两个不同的原子操作,则在写入和读取它们时都需要一个互斥体。互斥量不会阻止上下文切换,但以这种方式使用可以保证您不会访问半更新状态。

或者,如果您真的不想使用互斥锁,您可以使用单个 uint64_t 的 64 位中的某些位来存储计数(比如较低的 8 位)。 那么你会

m_cumulatedProcessingTime.fetch_add((processedTime << 8) + 1, boost::memory_order_release);

然后检索它

auto value = m_cumulatedProcessingTimeCount.load(boost::memory_order_acquire);
auto time = value >> 8;
auto count = value & 0xff;

像这样建立联合:

union CData {
  struct {
    __int32   a;
    __int16   c;
    __int16   d:2,
              e:14;
    // you get the idea...
  }
  __int64     n64;
}

使用结构成员的所有变量,用 n64 做原子的事情。

你可以,但你可能不想。

您需要一个 atomic shared_ptr 到包含两个变量的结构。读取的过程只是原子地复制 shared_ptr 并查看它指向的结构。写入过程为:

  1. 读取原子shared_ptr.

  2. 分配(使用 std::make_shared)一个新结构,其中包含基于结构中变量的两个变量的新值。

  3. 尝试一个原子 compare/exchange 来设置原子 shared_ptr 指向你的新结构。

  4. 如果 compare/exchange 成功,您就完成了。

  5. 释放您创建的新结构并返回到第 1 步。(保证其他作者取得了进展。)

虽然你真的不想这样做。就用一把锁吧。