我目前正在研究Spinlock类,并试图使其尽可能合理,主要是基于以下建议:https://software.intel.com/en-us/articles/implementing-scalable-atomic-locks-for-multi-core-intel-em64t-and-ia32-architectures
正在进行的工作如下所示:
class Spinlock
{
public:
Spinlock() : m_lock(false) {}
void lock()
{
// heavy test here for the usual uncontested case
bool exp = false;
if (!m_lock.compare_exchange_weak(exp, true, std::memory_order_acquire))
{
int failCount = 0;
for (;;)
{
// processor spin loop hint
_mm_pause();
// spin on mov instead of lock instruction
if (!m_lock.load(std::memory_order_relaxed))
{
// heavy test now that we suspect success
exp = false;
if (m_lock.compare_exchange_weak(exp, true, std::memory_order_acquire))
{
return;
}
}
// backoff (potentially make exponential later)
if (++failCount == SOME_VALUE)
{
// Yield somehow.
failCount = 0;
}
}
}
}
void unlock()
{
m_lock.store(false, std::memory_order_release);
}
std::atomic_bool m_lock;
};
但是,从那里轻松地读取内容似乎可以从理论上允许生成的代码做意外的事情,例如创建死锁:http://joeduffyblog.com/2009/02/23/the-magical-dueling-deadlocking-spin-locks/
这段代码不应像链接示例那样陷入僵局,因为外部获取应该防止松弛的负载向后移动,但是我实际上并没有处理所有可能存在的代码转换。我需要什么存储顺序和/或防护来确保此代码安全而不丢失性能?因为周围的内存顺序过于宽松,退回实现是否有可能比预期的发生更多或更少的频繁(>几次循环)?
在相关说明中,为什么网络上的自旋锁示例对自旋锁使用获取/释放内存顺序而不是顺序一致?我发现有一条评论说,允许自旋锁释放跨过以后的自旋锁获取可能会导致问题:http://preshing.com/20120913/acquire-and-release-semantics/#IDComment721195810
最佳答案
这段代码不应像链接示例那样陷入僵局,因为外部获取应该防止松弛的负载向后移动,但是我实际上并没有处理所有可能存在的代码转换。
获取操作保证编译器和CPU都不会在获取操作之前对后续的读取进行重新排序。
我需要什么存储顺序和/或防护来确保此代码安全而不丢失性能?
您在这里不需要任何额外的同步,您的代码可以完成正确的事情。
为什么网络上的自旋锁示例使用自旋锁的获取/释放内存顺序而不是顺序一致?
因为获取/释放语义足以实现互斥体。在某些体系结构上,顺序一致性操作比获取/发布更为昂贵。
我不能推荐足够的收看atomic<> Weapons: The C++ Memory Model and Modern Hardware,它详细介绍了此主题。
关于c++ - 如何安全地使用C++ 11为x86(-64)编写“测试和测试设置(TATAS)”自旋锁?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/29202475/