携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第10天,点击查看活动详情
Futex = Spin + Mutex(现代操作系统的做法)
关于互斥的一些分析
自旋锁 (线程直接共享 locked)
- 更快的 fast path
-
- xchg 成功 → 立即进入临界区,开销很小
- 更慢的 slow path
-
- xchg 失败 → 浪费 CPU 自旋等待
睡眠锁 (通过系统调用访问 locked)
- 更快的 slow path
-
- 上锁失败线程不再占用 CPU(会进入睡眠)
- 更慢的 fast path
-
- 即便上锁成功也需要进出内核 (syscall)
Futex: Fast Userspace muTexes
- Fast path: 一条原子指令,上锁成功立即返回(快的时候快点完)
- Slow path: 上锁失败,执行系统调用睡眠(慢的时候可以慢点,只要慢的时间足够少就好)
-
- 性能优化的最常见技巧
-
- 看 average (frequent) case 而不是 worst case
POSIX 线程库中的互斥锁 (pthread_mutex)
如下:
#include "thread.h" #include "thread-sync.h" #define N 10000000 spinlock_t lock = SPIN_INIT(); long n, sum = 0; void Tsum() { for (int i = 0; i < n; i++) { spin_lock(&lock); sum++; spin_unlock(&lock); } } int main(int argc, char *argv[]) { assert(argc == 2); int nthread = atoi(argv[1]); n = N / nthread; for (int i = 0; i < nthread; i++) { create(Tsum); } join(); assert(sum == n * nthread); } //这个时候多线程操作相应就会比较快,比自旋锁好。
-
- 观察系统调用 (strace)
- gdb 调试
-
- set scheduler-locking on, info threads, thread x
Futex: Fast Userspace muTexes (cont'd)(实现方法)
先在用户空间自旋
- 如果获得锁,直接进入
- 未能获得锁,系统调用
- 解锁以后也需要系统调用
//概念上的模型,模拟操作系统的行为(互斥协议是怎样在用户态和内核态进行协同的) class Futex: locked, waits = '', '' def tryacquire(self): if not self.locked: # Test-and-set (cmpxchg) # Same effect, but more efficient than xchg self.locked = '🔒' return '' else: return '🔒' def release(self): if self.waits: self.waits = self.waits[1:] else: self.locked = '' @thread def t1(self): while True: if self.tryacquire() == '🔒': # User self.waits = self.waits + '1' # Kernel while '1' in self.waits: # Kernel pass cs = True # User del cs # User self.release() # Kernel @thread def t2(self): while True: if self.tryacquire() == '🔒': self.waits = self.waits + '2' while '2' in self.waits: pass cs = True del cs self.release() @thread def t3(self): while True: if self.tryacquire() == '🔒': self.waits = self.waits + '3' while '3' in self.waits: pass cs = True del cs self.release() @marker def mark_t1(self, state): if localvar(state, 't1', 'cs'): return 'blue' @marker def mark_t2(self, state): if localvar(state, 't2', 'cs'): return 'green' @marker def mark_t3(self, state): if localvar(state, 't3', 'cs'): return 'yellow' @marker def mark_both(self, state): count = 0 for t in ['t1', 't2', 't3']: if localvar(state, t, 'cs'): count += 1 if count > 1: return 'red'
总结
本次课核心的问题
- Q: 如何在多处理器系统上实现互斥?
解决问题要先理解问题,提出假设,回到提出问题的一方,打破假设。
Take-away message
- 软件不够,硬件来凑 (自旋锁)
- 用户不够,内核来凑 (互斥锁)
- Fast/slow paths: 性能优化的重要途径(伸缩性)