【发布时间】:2017-01-30 01:56:37
【问题描述】:
假设用户正在转动 MIDI 控制器上的旋钮,并且这些值作为存储值的增量和减量发送到我的程序。单向转动旋钮将发送一系列减量,其值取决于旋转速度;扭转另一种方式增量。我想将存储的值(以及以下函数发出的值)保持在 0 到 100 之间。如果删除一条或几条消息,那没什么大不了的,但我不希望在OffsetResult_ 函数发出的值。
那么我的问题是——以下内存顺序指令看起来正确吗?对我来说最清楚的是compare_exchange_strong。该程序将其用作可能失败的store,因此似乎释放内存排序适用。
我什至可以去std::memory_order_relaxed,因为我主要关心的是对storedV 的更改的原子性,而不是记住对storedV 的每次更改?
有没有一种通用的方法来查看组合的加载/存储函数,以确定它是否应该是获取、释放或顺序一致的?
class ChannelModel {
ChannelModel():currentV{0}{};
int OffsetResult_(int diff) noexcept;
private:
atomic<int> storedV;
};
int ChannelModel::OffsetResult_(int diff) noexcept {
int currentV = storedV.fetch_add(diff, std::memory_order_acquire) + diff;
if (currentV < 0) {//fix storedV unless another thread has already altered it
storedV.compare_exchange_strong(currentV, 0, std::memory_order_release, std::memory_order_relaxed);
return 0;
}
if (currentV > 100) {//fix storedV unless another thread has already altered it
storedV.compare_exchange_strong(currentV, 100, std::memory_order_release, std::memory_order_relaxed);
return 100;
}
return currentV;
}
请注意,实际代码要复杂得多,有理由相信来自控制器的每条消息的响应将花费足够长的时间,以至于有时会由两个线程几乎同时调用此函数.
【问题讨论】:
-
1.
fetch_add(...) + diff添加了两次diff,不是吗? 2.fetch_add会不会导致storedV = storedV + diff可能是< 0或> 100进而可能对其他线程可见? -
fetch_add 返回原始值并存储加法的结果。因此,为了能够在函数中正确使用结果,而无需再次获取原子(并且可能存在并发问题),我还将 diff 添加到获取的值中。
-
是的,越界值可能对其他线程可见,但这不是主要问题,因为函数永远不会返回越界值。所以用户不会注意到问题,除了开始向相反方向移动控制器时可能会有非常轻微的延迟。我的目标是让每个线程都有一个内部一致的值视图——这就是为什么大部分工作都是使用临时变量完成的。
-
这是一个略有不同的问题域,但您可能感兴趣的是CRDTs
标签: c++ c++11 concurrency atomic compare-and-swap