复习
本次课回答的问题
本次课主要内容
理解并发的工具
互斥 (mutual exclusion),“互相排斥”
lock_t
数据结构和 lock/unlock
API:typedef struct {
...
} lock_t;
void lock(lock_t *lk);
void unlock(lock_t *lk);
一把 “排他性” 的锁——对于锁对象 lk
lock
失败的尝试
(部分) 成功的尝试
实现互斥的根本困难:
提出算法、解决问题 (Dekker/Peterson/...'s Protocols)
或者……
改变假设 (软件不够,硬件来凑)
LOCK
指令前缀例子:sum-atomic.c
sum = 200000000
Atomic exchange (load + store)
int xchg(volatile int *addr, int newval) {
int result;
asm volatile ("lock xchg %0, %1"
: "+m"(*addr), "=a"(result) : "1"(newval));
return result;
}
更多的原子指令:stdatomic.h (C11)
xchg
实现互斥如何协调宿舍若干位同学上厕所问题?
实现互斥的协议
int table = YES;
void lock() {
retry:
int got = xchg(&table, NOPE);
if (got == NOPE)
goto retry;
assert(got == YES);
}
void unlock() {
xchg(&table, YES)
}
int locked = 0;
void lock() { while (xchg(&locked, 1)) ; }
void unlock() { xchg(&locked, 0); }
并发编程:千万小心
原子指令的模型
486 (20-50MHz) 就支持 dual-socket 了
在 L1 cache 层保持一致性 (ring/mesh bus)
L1 cache line 根据状态进行协调
考虑常见的原子操作:
reg = load(x); if (reg == XX) { store(x, YY); }
reg = load(x); store(x, XX);
t = load(x); t++; store(x, t);
它们的本质都是:
LR: 在内存上标记 reserved (盯上你了),中断、其他处理器写入都会导致标记消除
lr.w rd, (rs1)
rd = M[rs1]
reserve M[rs1]
SC: 如果 “盯上” 未被解除,则写入
sc.w rd, rs2, (rs1)
if still reserved:
M[rs1] = rs2
rd = 0
else:
rd = nonzero
int cas(int *addr, int cmp_val, int new_val) {
int old_val = *addr;
if (old_val == cmp_val) {
*addr = new_val; return 0;
} else { return 1; }
}
cas:
lr.w t0, (a0) # Load original value.
bne t0, a1, fail # Doesn’t match, so fail.
sc.w t0, a2, (a0) # Try to update.
bnez t0, cas # Retry if store-conditional failed.
li a0, 0 # Set return to success.
jr ra # Return.
fail:
li a0, 1 # Set return to failure.
jr ra # Return
BOOM (Berkeley Out-of-Order Processor)
lsu/dcache.scala
s2_sc_fail
的条件性能问题 (0)
性能问题 (1)
性能问题 (2)
同一份计算任务,时间 (CPU cycles) 和空间 (mapped memory) 会随处理器数量的增长而变化。
使用场景:
作业那么多,与其干等 Online Judge 发布,不如把自己 (CPU) 让给其他作业 (线程) 执行?
“让” 不是 C 语言代码可以做到的 (C 代码只能计算)
syscall(SYSCALL_lock, &lk);
lk
,但如果失败,就切换到其他线程syscall(SYSCALL_unlock, &lk);
lk
,如果有等待锁的线程就唤醒操作系统 = 更衣室管理员
*lk = 🔒
,系统调用直接返回*lk = ✅
自旋锁 (线程直接共享 locked)
睡眠锁 (通过系统调用访问 locked)
小孩子才做选择。我当然是全都要啦!
POSIX 线程库中的互斥锁 (pthread_mutex
)
set scheduler-locking on
, info threads
, thread X
先在用户空间自旋
RTFM (劝退)
本次课回答的问题
Take-away message