真线程:不锁会丢,锁了不丢
(每道题开头都有同一段:上面的模拟器。判题机要确定的答案,所以本条用「调度表」代替真线程的运气。)
(这一节还带着真线程版:Counter 的 inc 是读-睡-写(conc_02 的写法),SafeCounter 用 with lock 包住它;hammer(counter, workers, times) 开几条线程各加 times 次,交回最终的 n。真线程的结果不确定,题里只比大小。)
两条线程各加 30 次,一次不锁一次锁,只比大小:
def inc_steps(k=1):
return [("read",), ("add", k), ("write",)]
def check_act_steps(need=1):
return [("read",), ("check", need), ("add", -need), ("write",)]
def run_schedule(threads, schedule, start=0):
"""threads: 每条线程是「步」的列表;schedule: 线程编号的序列,每个编号出现一次就走一步。
共享变量 n;每条线程有自己的寄存器 reg。交回 (最终 n, 记录列表)。"""
n = start
pc = [0] * len(threads)
reg = [0] * len(threads)
log = []
for t in schedule:
if pc[t] >= len(threads[t]):
continue
step = threads[t][pc[t]]
pc[t] += 1
n, reg[t], note, go_on = do_step(step, n, reg[t])
log.append("T" + str(t) + ":" + note)
if not go_on:
pc[t] = len(threads[t]) # 检查没过:这条线程后面的步全部跳过
return n, log
def do_step(step, n, r):
if step[0] == "read":
return n, n, "read " + str(n), True
if step[0] == "add":
return n, r + step[1], "add→" + str(r + step[1]), True
if step[0] == "write":
return r, r, "write " + str(r), True
if step[0] == "check":
ok = r >= step[1]
return n, r, "check " + str(r) + (">=" if ok else "<") + str(step[1]), ok
if step[0] == "atomic":
notes = []
ok = True
for s in step[1]:
n, r, note, ok = do_step(s, n, r)
notes.append(note)
if not ok:
break
return n, r, "atomic(" + ",".join(notes) + ")", ok
raise ValueError(step[0])
def all_schedules(a, b):
"""两条线程(a 步和 b 步)的全部交错:每个交错是一串 0/1。"""
if a == 0:
return [[1] * b]
if b == 0:
return [[0] * a]
return [[0] + s for s in all_schedules(a - 1, b)] + [[1] + s for s in all_schedules(a, b - 1)]
def outcomes(threads, start=0):
counts = {}
for s in all_schedules(len(threads[0]), len(threads[1])):
n, _ = run_schedule(threads, s, start)
counts[n] = counts.get(n, 0) + 1
return counts
def locked(steps):
return [("atomic", steps)]
import threading
import time
class Counter:
def __init__(self):
self.n = 0
def inc(self):
tmp = self.n
time.sleep(0.001)
self.n = tmp + 1
class SafeCounter(Counter):
def __init__(self):
super().__init__()
self.lock = threading.Lock()
def inc(self):
with self.lock:
super().inc()
def hammer(counter, workers=2, times=30):
def work():
for _ in range(times):
counter.inc()
ts = [threading.Thread(target=work) for _ in range(workers)]
for t in ts:
t.start()
for t in ts:
t.join()
return counter.n
a = hammer(Counter(), 2, 30)
b = hammer(SafeCounter(), 2, 30)
print(str(a < 60) + "/" + str(b == 60) + "/" + str(a <= b))
全部评论