Lock-Based/Lock-Free/Wait-Free之间区别

摘自 《The Art of Writing Efficient Programs》 https://learning.oreilly.com/library/view/the-art-of/9781800208117/

下面几个程序分别是 lock-based/wait-free/lock-free的

// ==== lock-based ====
std::mutex m;
size_t count;
{
  std::lock_guard l(m);
  ++count;
}

// ==== wait-free =====
std::atomic<size_t> count;
count.fetch_add(1, std::memory_order_relaxed);

// ==== lock-free =====
std::atomic<size_t> count;
size_t c = count.load(std::memory_order_relaxed);

while (!count.compare_exchange_strong(c, c + 1,
     std::memory_order_relaxed, std::memory_order_relaxed)) {}

大致区别如下所说:wait-free不需要等待,lock-free不需要显示锁但是会有等待,因为在不断地进行重试。

We have just seen examples of the three main types of concurrent programs:


UPDATE: <Lock-free programming with modern C++ - Timur Doumler [ACCU 2017]>

lock-free: at least one thread will always make progress

wait-free: all threads will always make progress


https://github.com/apache/incubator-brpc/blob/master/docs/cn/atomic_instructions.md

原子指令能为我们的服务赋予两个重要属性:wait-free和lock-free。前者指不管OS如何调度线程,每个线程都始终在做有用的事;后者比前者弱一些,指不管OS如何调度线程,至少有一个线程在做有用的事。如果我们的服务中使用了锁,那么OS可能把一个刚获得锁的线程切换出去,这时候所有依赖这个锁的线程都在等待,而没有做有用的事,所以用了锁就不是lock-free,更不会是wait-free。为了确保一件事情总在确定时间内完成,实时系统的关键代码至少是lock-free的。在百度广泛又多样的在线服务中,对时效性也有着严苛的要求,如果RPC中最关键的部分满足wait-free或lock-free,就可以提供更稳定的服务质量。事实上,brpc中的读写都是wait-free的,具体见IO。

值得提醒的是,常见想法是lock-free或wait-free的算法会更快,但事实可能相反,因为:

mutex导致低性能往往是因为临界区过大(限制了并发度),或竞争过于激烈(上下文切换开销变得突出)。lock-free/wait-free算法的价值在于其保证了一个或所有线程始终在做有用的事,而不是绝对的高性能。但在一种情况下lock-free和wait-free算法的性能多半更高:就是算法本身可以用少量原子指令实现。实现锁也是要用原子指令的,当算法本身用一两条指令就能完成的时候,相比额外用锁肯定是更快了。