原顺序是 free_user_thread 在前、state=THREAD_DONE 在后:在已释放用户栈/上下文但 state 仍为 RUNNING 的窗口里,若发生一次再调度(定时器 IRQ → try_reschedule → _yield 仅按 state 过滤),本线程会被再次选中并带着失效上下文回用户态,在已回收的栈上取指 0(现场 [0E] rip=0x0 err=0x15 cr2=0x0 rsp=0x7fffffde0000,紧跟 [syscall] exit called)。_yield 的选择逻辑(THREAD_READY/RUNNING 才可选)本身已正确,故只需把死亡标记提前。