渐渐能理解为何不愿意雇佣35岁以上程序猿。去年换了份工作,组里4位组员其中3位40+,发现其实最大的问题并不是说精力不济卷不动
刚看到个贴子,说博主去年跳槽,新组里4个人有3个40+程序员,他说这段时间渐渐理解为啥很多公司不愿意招35+。
网友回帖有骂老板嫌老爱用廉价年轻人的,也有替中年程序员抱不平,说人家经验宝贵。
我觉得这事吧,关键真不在“精力不行卷不动”,而是心态和更新速度。有的前辈业务很熟,但抗拒新技术,开会一言不合就摆资历,当自己是“顾问”,不太愿意背锅干脏活累活;遇到变动,第一反应是抱怨,而不是想怎么升级自己。
换个角度想,公司算的是性价比:给到这个年薪,你是不是还在持续产出、肯学肯变通、能带团队。
说到底,年龄只是放大镜,放大的是之前几年你对自己的投资。与其怕35+被淘汰,不如从现在开始,让自己哪怕40岁,依然是那个“最难被替代的人”。
我为大家打造了一份RPA教程, 完全免费: songshuhezi.com/rpa.html 🔥 虎哥私藏精品 🔥 虎哥作为一名老码农,整理了全网最全 《python高级架构师资料合集》 ,总量高达 650GB
算法题: H2O 生成
昨天晚上十一点多,我正准备关电脑睡觉,我们组那个小李突然给我丢了个截图过来,说:“东哥,这个 H2O 生成到底咋写啊,用 python 的那个多线程版本,我脑子已经浆糊了”。我一看,嗨,这不是面试高频题嘛,干脆就顺嘴跟你们也唠一遍。 用大白话说一下哈,不照着题面背。- 你有一堆线程,有的负责“打 H”,有的负责“打 O”
-
每个氢原子线程会调用
hydrogen()方法,方法里会执行一个releaseHydrogen(),你可以理解成打印"H" -
每个氧原子线程会调用
oxygen()方法,里面执行releaseOxygen(),理解成打印"O" -
要求是:
输出要按水分子来凑
,也就是每 3 个字符必须刚好是 2 个
"H"和 1 个"O",顺序不限,比如HHO、HOH都行,但不能出现HHH或者OOOO这种乱的
h_count = 0
o_count = 0
然后:
def hydrogen(...):
global h_count
h_count += 1
releaseHydrogen()
# 当 h_count == 2 and o_count == 1 的时候,说明可以组成一杯水
看着好像有点道理对吧?但问题一大堆:
-
h_count += 1这种操作在多线程下本身就不是原子的,会冲突 - 即便你加了锁,还得想办法“卡住”多余的 H 或 O,不让他们乱印
- 还要保证老的一杯水凑齐了,再开始下一杯,不然很容易变成“2 杯水掺成一盆水”
- 氢线程 要进来打字,必须先拿到一张“氢票”
- 氧线程 要进来打字,必须拿到一张“氧票”
- 每次配水的流程是:允许 2 张氢票进来 + 1 张氧票进来,三个人都干完活,再发下一波票
Semaphore
(信号量)。
我们来直接看一版代码,然后再拆开聊,每行都别紧张,看几遍就顺了。
import threading
from typing import Callable
class H2O:
def __init__(self):
# 一开始允许 2 个 H 先来
self.h_sem = threading.Semaphore(2)
# O 一开始不能来,等 H 凑够了再放行
self.o_sem = threading.Semaphore(0)
# 记录当前这一杯水已经来了几个 H
self.h_count = 0
self.lock = threading.Lock()
def hydrogen(self, releaseHydrogen: Callable[[], None]) -> None:
# 先抢一张 H 的“门票”
self.h_sem.acquire()
# 真正打印 H
releaseHydrogen()
# 更新“这一杯水已经来了几个 H”这个状态
with self.lock:
self.h_count += 1
# 如果已经有 2 个 H 了,就可以放行 1 个 O 进来了
if self.h_count == 2:
# 允许一个 O 线程通过
self.o_sem.release()
def oxygen(self, releaseOxygen: Callable[[], None]) -> None:
# 没有人放行之前,O 是不能随便来的
self.o_sem.acquire()
# 真正打印 O
releaseOxygen()
# 这一杯水完工了,状态重置,为下一杯水发票
with self.lock:
# 当前这杯水的 H 用完了
self.h_count = 0
# 发 2 张新 H 票,供下一杯水使用
self.h_sem.release()
self.h_sem.release()
你可以在脑子里模拟一轮:
-
刚开始
h_sem里有 2 张票,所以最多两个 H 线程能通过acquire,第三个 H 会在这里堵着 -
前两个 H 执行完
releaseHydrogen()之后,会把h_count加到 2 -
第二个 H 把
h_count改到 2 的时候,会self.o_sem.release(),等于发了一张 O 的票 -
某个 O 线程拿到这张票,
acquire成功,然后打印 O -
O 打完之后,重置
h_count = 0,再放出 2 张 H 票,下一杯水又可以开始凑了
- 每次 至少 要有 2 个 H + 1 个 O 才能一起完成一个循环
-
H 多了会在
h_sem.acquire()这里排队,O 多了会在o_sem.acquire()这里排队 - 任何时候,你往输出上面看,一定是按 3 个字符一组,每组都刚好“2H + 1O”
为啥还要那把 lock?
有人看到这里会问一句:“既然
h_sem
控制 H 的数量了,那
h_count
还要不要加锁啊?”
要的。
原因是:
self.h_count += 1
不是原子操作,两个 H 线程几乎同时修改这个值,会导致:
- 一个线程读到旧值
- 两个线程分别写回去
- 最后结果变成 1,而不是你以为的 2
with self.lock:
把这个“读 + 改 + 判”的一套动作包成一个不可拆的小块,保证一次只有一个线程在里面操作。
在多线程的世界里,一个很重要的习惯就是:
只要有共享状态,就先想锁,再想逻辑
,不然 bug 会非常阴间,而且不好复现。
你要是想本地跑一跑,可以自己写个小 main,随手贴一个简化版的测试:
import threading
import random
import time
def test():
h2o = H2O()
def printH():
print("H", end="")
def printO():
print("O", end="")
threads = []
# 随便造一点 H、O 线程,数量要满足 2:1
for _ in range(10):
t = threading.Thread(target=h2o.hydrogen, args=(printH,))
threads.append(t)
for _ in range(5):
t = threading.Thread(target=h2o.oxygen, args=(printO,))
threads.append(t)
# 打乱顺序,更接近真实场景
random.shuffle(threads)
for t in threads:
t.start()
# 故意加一点点随机延迟
time.sleep(0.01)
for t in threads:
t.join()
if __name__ == "__main__":
test()
你随便跑几次,可能会看到这样的输出:
-
HHOHHOHHOHHOHHO -
或者
HOHHOHHOHHOHHO
我为大家打造了一份RPA教程, 完全免费: songshuhezi.com/rpa.html 🔥 虎哥私藏精品 🔥 虎哥作为一名老码农,整理了全网最全 《python高级架构师资料合集》 ,总量高达 650GB