单核上的多线程-Python中的 GIL
Created|Updated
|Word Count:191|Reading Time:1mins|Post Views:
GIL (Global Interpreter Lock)的存在虽然无法利用多核,但是可以勉强让系统在在单核上,任何一个线程使用过多时间片/主动放弃 CPU 的时候,让其他线程上下文切入进来。算是尽量跑满CPU吧。Python中的对象很多都是默认线程安全的,GIL的这种不可见的特性,让很多旧的程序依赖起 GIL,以至于无法从Python中移除掉它。GIL 的存在,让 Python 特别适合跑 Nodejs 爬虫一样的 IO 密集型(IO-bound)任务,反而不适合跑CPU 密集型任务(CPU-bound)。但实际上这种混蛋多线程的形式,恐怕还不如 EventLoop 的 Nodejs,因为多了很多 Context Switch 的代价。
Author: magicliang
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
Related Articles
2026-05-21
下推自动机:多一只栈就能处理嵌套
DFA 和 NFA 都只有有限状态。状态数量再多,也仍然是有限的。它们适合识别固定模式、分支和重复,但面对任意深度的嵌套结构时会遇到硬边界。 括号匹配就是最小例子。()、(())、(()()) 都合法,(()、())、)( 都非法。有限状态机可以识别“最多三层括号”“最多十层括号”,但只要深度没有上限,有限状态就不够用了。机器需要一种能随输入增长的记忆结构。 下推自动机(Pushdown Automaton,PDA)给有限状态机加了一只栈。状态仍然有限,但栈可以随着输入增长。读到左括号就压栈,读到右括号就弹栈;输入结束后,栈如果回到底部符号,括号就匹配完成。 栈解决的是后进先出问题 括号嵌套天然是后进先出结构。最近打开的左括号,必须最先被右括号关闭。 123456789(()())1234561: push (2: push (3: pop (4: push (5: pop (6: pop ( 第 2 个字符打开的括号,要在第 3 个字符关闭;第 4 个字符打开的括号,要在第 5 个字符关闭;第 1 个字符打开的括号最后关闭。这个顺序正好是栈。 有限状态机无法保存任意多个...
2026-05-20
AgentScope 全景实战:设计速读与生产级十二层装配
把 AgentScope 当成"另一个 LangChain"是上手时最常见的误区。AgentScope v1.x 在 2025 年中做过一次实质上不向后兼容的 API 重构——v0.x 的 actor-based 多机分布式被整体删除,新核心改为 async-ReAct 循环 + MsgHub 显式消息广播。它的设计取向与 LangChain / LangGraph 也不同:假设开发者从一个 ReActAgent 开始,逐层往外加东西——先有一个 agent 能跑,再共享 LLM 单例,再装工具,再装 MCP,再往上做多 agent 路由,最后接观测和部署。这种"渐进式装配"的次序不是教学方便,是它的实际工程姿态——每一层都能独立验证、独立失败、独立替换。 本文分两部分。第一部分是设计速读:把 AgentScope v1.x 的范式翻转、ReAct + MsgHub 核心抽象、与 LangGraph / CrewAI / MAF 的对位、A2A + MCP 协议化分布式、Trinity-RFT 训练集成讲清楚——理解了"为什么&q...
2026-05-20
指称语义:把程序翻译成 Python 函数
前两篇用操作语义解释程序。大步语义直接给最终结果,小步语义展示每一步规约。第 2 章还有第三种视角:指称语义(denotational semantics)。 指称语义把程序映射到某个更基础的对象。在本文的小语言里,表达式会变成一个 Python 函数,语句也会变成一个 Python 函数。程序的含义从“机器怎样跑”转到“它等价于哪个函数”。 对 Java 程序员来说,可以先把它理解成: 12Function<Environment, Value> // 表达式的含义Function<Environment, Environment> // 语句的含义 本文继续沿用前面的 AST,用 Python 函数重写这套小语言的含义。 三种语义回答三类问题 同一段程序可以从三种角度解释。 语义 关心的问题 本系列里的实现 大步语义 运行完成后得到什么 递归解释器 小步语义 每一步怎样变化 规约机器 指称语义 程序等价于什么对象 函数翻译器 大步和小步都属于操作语义。它们会描述程序在某种抽象机器上怎样执行。指称语义换成另一...
2026-05-21
抽象解释:用保守近似理解程序
停机问题说明,对任意程序做完备且正确的停机判断是不可能的。工程分析工具并没有因此失去意义。它们换了目标:不追求精确理解所有程序,而是用可计算的抽象信息给出有用结论。 抽象解释就是这个方向的代表。它把具体值集合映射到抽象域里,用保守规则模拟程序执行。只要抽象规则覆盖了所有可能的具体行为,分析结果就可以用于告警、优化或拒绝高风险程序。 本文用区间分析做一个最小模型:变量不再保存单个整数,而是保存可能取值范围。 从具体值到抽象值 具体执行关心单个值: 123x = 3y = 5z = x + y = 8 抽象执行关心值的集合。若只知道 x 在 0 到 10 之间,y 等于 5,就可以推出: 123x in [0, 10]y in [5, 5]z = x + y => z in [5, 15] 这个结果不精确。z 不一定等于区间里的每个数,但所有可能结果都被包含在 [5, 15] 里。保守性比精确性更重要:不能漏掉可能发生的值。 区间抽象域 区间可以用两个端点表示。 12345678910111213141516from dataclasses import dataclass@...
2026-05-21
停机问题:为什么某些判断没有通用程序
上一篇文章用寄存器机解释器说明了通用性:程序可以编码成数据,解释器可以读取这份数据并模拟执行。通用性带来强大表达能力,也带来一个边界问题:能不能写一个程序,判断任意程序在任意输入上是否会停机。 答案是否定的。停机问题说明,不存在一个对所有程序和输入都正确的通用停机判定器。工程里可以做超时、步数限制、静态分析和资源预算,但这些方法要么不完整,要么会保守拒绝,要么只覆盖有限范围。 本文先用一个可运行的有界检查器展示工程近似,再用自引用反证解释为什么通用判定器不存在。 问题形式 停机问题可以写成一个理想函数: 1halts(program, input_data) -> True / False 它的承诺是: 返回值 含义 True program(input_data) 最终会停机 False program(input_data) 会一直运行 难点在“任意程序”和“任意输入”。判断某个具体程序很容易;判断所有程序则会遇到自引用。 工程里常见的近似:有界执行 最朴素的方法是只运行有限步。如果步数内停机,就返回 True;如果步数用完还没停,就返回 Fa...
2026-05-21
确定性有限自动机:状态、输入和接受条件
上一篇用指称语义把程序翻译成 Python 函数。程序语义这一段到这里已经形成闭环:AST 给出结构,大步语义、小步语义和指称语义分别给出不同角度的含义。接下来进入自动机。问题从“程序怎样执行”换成“机器怎样根据输入移动到下一个状态”。 确定性有限自动机(Deterministic Finite Automaton,DFA)是自动机部分的起点。它没有变量表,没有栈,没有堆,也没有可写纸带;它只记住一个当前状态,然后逐个读取输入字符。每读一个字符,机器就根据一张规则表换到下一个状态。输入读完后,当前状态如果属于接受状态,字符串就被接受;否则被拒绝。 这个模型很小,却足够解释很多工程直觉:订单状态流转、协议解析、词法分析、简单规则匹配,都可以先压成“当前状态 + 输入 -> 下一个状态”的形式。 从业务状态机收缩到形式模型 Java 程序员对状态机并不陌生。订单可以从 CREATED 变成 PAID,再变成 SHIPPED;审批单可以从 DRAFT 变成 SUBMITTED,再变成 APPROVED 或 REJECTED。这些状态机通常带着业务动作、数据库事务、权限检查、通知...
Announcement
人生只是,守株待兔
