三个容易混用的词

并发关注多个任务如何交错推进,并行关注是否同时执行,异步关注发起操作后如何接收完成结果。事件循环可以处理很多等待中的请求,但长时间占用执行线程的计算仍会影响其他任务。

进程拥有隔离的执行环境,线程共享所在进程中的部分资源。操作系统调度决定任务何时获得 CPU。线程数量增加并不保证吞吐提高;切换、竞争与内存占用都可能成为成本。

读改写不是一个动作

把计数器加一拆开看,是读取旧值、计算新值、写回。两个任务可能都读取同一个旧值,随后互相覆盖。需要围绕业务不变量选择锁、原子操作、消息传递或数据库事务。

from concurrent.futures import ThreadPoolExecutor
from threading import Lock

counter = 0
lock = Lock()

def increment(_):
    global counter
    with lock:
        counter += 1

with ThreadPoolExecutor(max_workers=4) as pool:
    list(pool.map(increment, range(1000)))
assert counter == 1000

此例用锁明确保护共享状态;不要依赖某个解释器或某次调度恰好没有暴露竞争。跨进程或跨服务器的状态需要对应范围的协调机制,进程内锁无法保护远端数据库。

退出与过载也是并发设计

任务要有超时、取消和资源释放路径。队列需要容量上限与过载策略,避免生产速度持续超过消费速度。死锁排查要看持有资源和等待资源形成的关系;统一加锁顺序可以消除一类循环等待。

练习与面试自查

把上例改为每个任务先计算局部结果,再统一汇总,比较共享状态的数量。再设计一个容量有限的工作队列,说明满了以后阻塞、拒绝或丢弃会如何影响调用方。

能解释“异步函数里做重计算为什么仍可能卡住服务”,以及“设置超时为什么不代表远程任务已经被撤销”。

参考资料