刚看到个吐槽贴:团队来了个博士,同事代码写得乱七八糟,需求没弄懂就开干,留下不少坑,最后效果也就那样。可领导依旧最看重他,开会只点名夸博士,楼主觉得学历面前啥都白搭。

我觉得这事吧,先认个现实:在不少公司,学历就是块招牌。领导对上汇报、对外吹牛,一行“团队有博士”确实比你解释半天好用。网友回复有人骂领导眼瞎,有人说“社会就是看学历”,但这只能算职场的一部分。
从我角度看,关键还是别被气情绪化。你觉得自己干得更细,就把需求梳理、代码质量、风险提示这些事做到位,让谁在埋雷、谁在排雷一眼能看出来。
规则短期改不了,那就先把自己的价值做扎实,哪天机会来了,学历也拦不住你。
那我就直接开整了哈。
那天晚上下班有点晚,在公司楼下等外卖,手机上刷到一眼这个“交替打印 FooBar”的题,我心里想,这不就是面试官最爱拿来判断你会不会多线程同步的那个小玩具嘛,结果真去写的时候,很多人还是能写出一堆坑来。
先说下题目,大白话就是:有一个类 FooBar,里面有个整数 n,有两个线程,一个只负责打印 "foo",另一个只负责打印 "bar",要求他们俩配合着,最后输出变成:"foobarfoobarfoobar..." 总共 n 次。重点是“交替”这俩字,不是你随缘打印。
这个题如果你直接两个线程各自 for 一下,然后 print("foo") / print("bar"),那输出顺序肯定是乱的,因为线程调度你控制不了嘛,所以核心就是:怎么让两个线程乖乖排队,一个说完 foo,另一个马上接 bar。
我先用 Python 里最常见的 Condition 版本,逻辑很像两个人抢话筒,一个说完了把话筒交出去。
import threadingclassFooBar:def__init__(self, n: int): self.n = n self.cond = threading.Condition() self.foo_turn = True# 轮到谁说话,True 表示该 foo 了deffoo(self, printFoo):for _ in range(self.n):with self.cond:# 不是我说话的轮次,就老老实实等whilenot self.foo_turn: self.cond.wait()# 真正打印 printFoo()# 轮到 bar 了 self.foo_turn = False# 叫醒对方 self.cond.notify()defbar(self, printBar):for _ in range(self.n):with self.cond:while self.foo_turn: self.cond.wait() printBar() self.foo_turn = True self.cond.notify()外面启动线程大概是这样:
defprintFoo(): print("foo", end='')defprintBar(): print("bar", end='')if __name__ == "__main__": fb = FooBar(3) t1 = threading.Thread(target=fb.foo, args=(printFoo,)) t2 = threading.Thread(target=fb.bar, args=(printBar,)) t1.start() t2.start() t1.join() t2.join()# 输出:foobarfoobarfoobar这个写法几个点你心里要有数:
foo_turn 是一个“小白板”,写着“现在轮到谁了”while 那一段特别关键,不能用 if,不然被意外唤醒(spurious wakeup)的时候可能顺序乱掉notify() 只负责“喊一嗓子”,到底谁醒、什么时候醒,还是系统说了算,所以我们才要配合 while 再检查一次条件很多人第一次写会犯两个错: 一个是忘了 while,另一个是 printFoo() 放在锁外面,结果中间被插队,顺序就废了。这个跟我之前排查 TCP 粘包那次很像,看着都在收发数据,结果长度对不齐,最后定位到协议读写不对齐,抓包抓到人快没耐心了那种感觉
如果你嫌 Condition 这套有点啰嗦,其实可以用两个信号量 Semaphore,思路更直观一点:一开始只有 foo 那个信号量有“通行证”,bar 的是 0,所以只能等。
import threadingclassFooBar:def__init__(self, n: int): self.n = n self.foo_sem = threading.Semaphore(1) # 先让 foo 先走 self.bar_sem = threading.Semaphore(0) # bar 先堵住deffoo(self, printFoo):for _ in range(self.n): self.foo_sem.acquire() # 等轮到自己 printFoo() self.bar_sem.release() # 放行 bardefbar(self, printBar):for _ in range(self.n): self.bar_sem.acquire() printBar() self.foo_sem.release()这个看着就像两个人在门口对暗号:foo 说完,给 bar 一张票;bar 说完,再把票还给 foo。票就是信号量里那个计数。
这类题你要是只会写一个版本,面试官一追问“如果换成别的同步工具呢”,很容易就没话说了。Python 这边常用的同步手段其实就这几家伙:Lock / RLock、Condition、Semaphore、Event。这种轻量排序问题,用 Semaphore 或 Condition 就够了,别上来搞什么自旋 + 睡眠,那是浪费 CPU。
还有一个点很多人容易忽略:print 默认是带锁的,但只保证一次 print 里的内容不会被别的 print 打断,不保证多次 print 的相对顺序。所以我们才会封装 printFoo / printBar 由题目统一调用,而不是直接在线程里乱写 print("foo")、print("bar"),不然你自己也不好控制外面的输出逻辑。
现实里多线程问题比这个脏多了,比如数据库、MQ 那些场景,线程一多、锁一多,出问题就不是“多打印了一个 bar”这么简单了,可能直接锁死、性能掉到地板。这个小题其实就是个玩具版场景,帮你把“我先你后”“大家别说话一起抢”的节奏练熟。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html
🔥虎哥私藏精品🔥