Go 23:请求取消与任务取消为何分别建模
上篇用 POST /tasks 返回 202 和任务 ID,后台执行不占住提交请求。本章要解决另一半:客户端放弃一次 HTTP 请求、用户主动要求取消任务、服务器停止接收请求,是否都会让那个任务退出?答案只能由实际 context 所属者、记录提交点和 worker 的退出证据给出。在同一个累计工程里增加按 ID 取消接口,保留原 worker、队列和存储;完整工程包、SHA-256与解包复跑保留测试原始结果。 一条任务不能借用短命的请求 context 固定 Go 1.27.1 的 Request.Context 文档规定:对入站请求,客户端连接关闭、HTTP/2 请求取消,或 handler 返回,都可能取消该 context。POST /tasks 的 handler 本来就要在任务完成之前回 202。若把 request.Context() 存进队列,正常应答结束也会变成任务取消信号。这个 context 只属于本次请求的解析与应答,不属于已经被服务接受的业务任务。 现在每次接受任务都从执行器 context派生一个逐任务 context,并保留相应 CancelFunc...
Go 22:HTTP 提交、查询与非法输入怎样表达
上篇给同一执行器的提交与关闭设了协议。HTTP 接入时,最容易写错的不是路由语法,而是把 POST 的响应当成后台计算完成的凭据。任务一旦被执行器接收,就归本次服务所有;handler 返回、请求 context 被取消,不能直接解释成该任务已经退出。本章在同一个累计工程里添加 HTTP 适配层,以真实 loopback 请求测试提交、查询和错误输入;完整工程及实验包、SHA-256与解包复跑说明可独立核对。 三条路由各自交付什么 HTTP 里方法与路径要一起看。当前路由是 POST /tasks、GET /tasks/{id}、GET /health,由固定 Go 1.27.1 的 http.ServeMux 方法模式注册。POST 解一个 JSON 对象,字段 text 非空;接受时返回 202 Accepted,响应体只给 {"id":"task[0]"},Location: /tasks/task[0] 指出后续查询地址。202 说明本次非阻塞提交已经进入服务拥有的执行器,不说明统计完成,更不说明客户端一定收到了完整响应。GET /...
Kotlin 02:分支推导与智能转换的稳定前提
一个属性刚被检查为非空,为什么紧接着取它的长度,编译器却说“不可能智能转换”?因为检查时和使用时读到的可能不是同一个值。输入解析也有相似的误会:一个 for 循环计数到了两条文本,并不意味着系统已经提交了两条任务。要说明代码结果,得分别交代分支给出的值、循环改变的局部变量和属性读取发生的时间点。 本篇在 命令行基线 与 可变性实验 上,把 submit、help、batch 收进同一个解析入口,随后用可控 getter 检查智能转换的稳定性。版本仍是 Kotlin 2.2.21、Gradle 8.14、JDK 21;batch 目前只校验输入并报告本地计数,既没有执行者,也没有保存状态。 分支负责给出值,循环只改局部计数 前一章的 submitTask(args) 保持原有约定:只接受 submit <task text> 两个参数。新函数 parseCommand(args) 选择命令分支: 12345678910111213141516171819fun parseCommand(args: Array<String>): String {...
Go 21:服务关闭怎样完成停止接收、取消与等待
上篇限制了同时执行和排队的数量。队列有了上限,另一个问题就无法回避:调用方准备退出时,多个提交者仍可能正在等待空槽。若一个 goroutine 关闭输入 channel,另一个 goroutine 恰好发送,关闭过程本身便可能 panic。Close、cancel、等待退出必须各有责任,本篇在同一个累计工程的原 worker 路径上补关闭协议。完整工程与实验包、SHA-256和解包复跑说明保存本章实际代码及正反证据。 能关闭 channel 的不应是任何一个提交者 Go 1.27.1 的语言规范 Send statements 明确:向已关闭的 channel 发送会触发运行时 panic。testdata/send-after-close.go 是单独构建的故意错误程序;本次直接执行二进制得到 panic: send on closed channel,退出 2。这不是生产执行器发生的故障,而是要排除的错误设计。只在发送处先检查一个 closed 布尔值也不够:从检查通过到真正发送之间,另一 goroutine 仍可把 channel 关闭。 原批处理只有一个生产者,由它关闭输...
Python 39:同一个任务应用怎样结课
脚本目录里的测试全绿之后,仍有一个问题:换一台只拿到 wheel 的干净解释器,能不能提交、运行、查询、取消,并在失败时说清状态和资源归属?结课不能只靠 PYTHONPATH=src 下的回归。2026-10-12 从当前累计源码重新构建 wheel 和 sdist,用哈希锁安装构建工具;在仓库外创建不含 pip 的目标 venv,用独立 uv 安装 wheel,执行时移除 PYTHONPATH。 新 wheel 的 SHA-256、成员列表及每个 tasklab/*.py 与当前源码相同的核对结果在 examples/python-language/evidence/completion/acceptance/wheel.json。不是仅凭 py3-none-any 标签推断所有运行时都兼容:普通目标是 macOS arm64 CPython 3.12.3,模块路径指向目标 venv 的 site-packages。additional/source-is-not-installation.stderr 则保存故意从源码导入后安装位置断言失败的负例,退出码 1。 每种入口返回...
Go 20:有界 worker pool 怎样限制并发与排队
上篇让生产者和消费者退出时都有明确的等待者,但原来只有一个消费者、容量为 1 的输入 channel。给它加大缓冲并不能自动限制同时运行的工作:执行数由消费者数量决定,排队数由 channel 容量决定,这两个上限回答的是不同问题。本章给同一个任务工程的原队列加入固定 worker 数和可选的排队容量,用可控闸门检查峰值、满载和取消。完整工程及实验包、SHA-256与解包指令可复核,不需要另建一份演示工程。 容量 1 到底约束了什么 设两个 worker、输入 channel 容量 1。两个 worker 已分别取出一项并开始统计时,队列还可以保留一项。此时系统最多有三个已交给执行器、尚未结束的任务:两个正在执行,一个等待领取;不是“只有一个任务能进入系统”。接收端取走 channel 中的一项,那个等待槽位才重新可用。不能只用 len(inputs) 算所有占用,因为 worker 领取后元素已经不在 channel 中。 工程的 processTasksContextWithLimits 收到 workerCount、queueCapacity,分别初始化 workerCou...
Python 38:从语言规则到 CPython 能推多远
count_words 的运行结果是 {"one":2,"two":1}。dis 又显示 FOR_ITER、BINARY_OP 和调用相关指令。那么 BINARY_OP 是 Python 语言规范承诺的实现方式吗?不是:词频结果与对象、迭代协议属于可向语言规则求证的行为;字节码指令、解释器执行循环和 GIL 都是本次冻结的 CPython 构建需要另外核实的实现细节。00 的冻结记录给出 /usr/bin/python3.12、CPython 3.12.3、Ubuntu 补丁包版本 3.12.3-1ubuntu0.17,因此不能只查滚动的主分支源码。 本次重新按 00 的 .dsc 和两份归档 SHA-256 核对源包,使用 dpkg-source -x 解出 Ubuntu 补丁后的源码,并逐一比较 Python/ceval.c、Python/generated_cases.c.h、Python/ceval_gil.c、Include/internal/pycore_gil.h 与原始 v3.12.3 tar 的 SHA...
Go 19:WaitGroup、Once 与 atomic 各负责什么
上篇用一把 Mutex 保护任务记录的状态、结果和取消请求。现在服务还有三件性质不同的事:要等生产者和消费者整组退出,要保证同一份配置只初始化一次,还想从另一条路径读取“已有多少任务到达终态”。把这三件事都压到同一个原语上,往往会丢掉真正需要的约束。本章继续扩展同一个累计任务工程:生产者/消费者用 WaitGroup,配置用 Once,独立终态数用 atomic;记录的多字段提交仍由 Mutex 决定。完整工程及实验包与SHA-256可独立解包复跑。 独立复跑步骤列出本章正反例、源码校验及运行命令。 等整组退出,不等于请求整组停下 第 17 篇在队列里分别建了 producerDone 和 workerDone。本章让同一个队列在启动两个 goroutine 之前执行 workers.Add(2),生产者和消费者各自 defer workers.Done();调用方排空 outputs 后再 workers.Wait(),确保两者都完成自己的尾部工作。子统计 goroutine 仍由先前的单项 done 握手确认完成:WaitGroup 负责哪两个成员,应写在工程边界里,不能把没...
Python 37:对象释放为何不等于内存立刻下降
读完文件并释放字符串后,tracemalloc 跟踪的当前分配量几乎回到起点,进程 RSS 却不一定回到读文件前。这里有两种不同的问题:Python 对象是否仍被引用,以及分配器和操作系统当前把多少内存记在进程名下。仅凭其中一个指标,不能推断另一个必须同步下降。 16 的文件作用域提供 open_word_file,本篇用同一份合成输入比较 source.read() 全量持有字符串与 open_word_file 按行词元化。文件是 30000 行 One two one、360000 字节,两边结果都为 {"one":60000,"two":30000},且退出作用域后 source.closed 均为真。每种模式在独立子进程测量,以免前一模式的 RSS 高水位污染后一模式;写入输入文件后才开启 tracemalloc,只测读取和统计路径。 三种内存数字不互相代替 当前 PyPy 8.0.0 归档没有 _tracemalloc,但提供 gc.get_stats(memory_pressure=True)。脚本在 P...
Python 36:一次快了能说明优化有效吗
四次词频任务,某次顺序运行只用了约 0.13 秒,某次却用了 0.18 秒。单次相差已足以淹没很多所谓“小优化”。在比较 29 的线程、30 的进程和 31 的 asyncio前,必须先定义测量包含哪些步骤。 benchmarks/36_measure.py 为四种策略都提交四个 word_count("One two one"),每项执行 cpu_work(..., iterations=20000),并断言结果是 {"one":2,"two":1}。每种策略先跑一次不计入统计的暖机,再取五次原始样本;计时覆盖新建 store、提交任务、执行器或进程池启动、计算、等待和关闭。输出每次 perf_counter_ns 的 wall time、process_time_ns 的父进程 CPU time、每秒完成任务数、完成和失败数。进程模式的子进程 CPU 不在父进程 process_time_ns 里。 先看测到了什么 这次在 /usr/bin/python3.12 的 CPython 3.12.3...










