# 线程与异步模块 **脚本入口:** `eve.Thread()` 使用线程池执行原生安全任务,通过 Channel/Event 把结果送回主线程。 ## 基本用法 ```squirrel local threads = eve.Thread(); local channel = threads.newChannel(); local pool = threads.newThreadPool(2); pool.submitPush(channel, "done", 100); ``` ## 对象关系与调用时机 `Thread` 创建 ThreadPool 与 Channel;Pool 返回 Task;Channel 是 worker 到主线程的字符串通道。脚本 VM 和绝大多数引擎对象只允许主线程访问。 ## 目标导向指南 ### 把耗时任务移出主线程 创建线程池和 Channel,用 `submitSleep` / `submitPush` / `submitPost` 提交受支持任务;主线程轮询 Channel 或 Event 获取结果。worker 不得访问 Squirrel VM、窗口或 GPU 对象。 ### 等待关卡加载任务 需要观察状态时为每项任务保留 Task,轮询 `isDone()` / `hasFailed()`,显示进度。脚本提前释放 Task 或匿名 Channel 不会中止已提交工作;线程池会持有任务完成所需的原生状态。仅在退出或确定不会卡住界面时调用 `wait()` / `waitAll()`。 `newThreadPool(0)` 使用硬件并发度;显式线程数上限为 256。`waitAll()` 与 `stop()` 不能从同一个线程池的 worker 中调用,这类调用会立即抛出异常,避免自等待死锁。 ## 常见问题 - worker 调用 gfx/ui/Squirrel:这些对象不是线程安全的。 - worker 调用所属 Pool 的 `waitAll()` / `stop()`:会抛出异常;把关闭与汇合操作放在主线程。 - 主线程立即 `waitAll()`:异步退化成卡顿的同步。 - 忽略 `hasFailed()` / `getError()`:失败任务会悄悄丢结果。 - 并发任务使用相同 Event 名称:业务监听仍按队列消费;`asyncPost()` 的 Promise 按具体 Task 关联,不会因同名事件乱序而串线。 ## API 快查 下列方法名来自当前 Squirrel 绑定;同一模块创建的辅助对象(例如 `World`、`Body`、`Source`)的方法也列在这里。 - `clear()`、`demand()`、`getChannel()`、`getCount()`、`getError()`、`getHardwareConcurrency()`、`getName()`、`getPendingCount()` - `getPool()`、`getStatus()`、`getWorkerCount()`、`hasData()`、`hasFailed()`、`isDone()`、`isRunning()`、`newChannel()` - `newThreadPool()`、`pop()`、`postMain()`、`push()`、`stop()`、`submitPost()`、`submitPush()`、`submitSleep()` - `supply()`、`wait()`、`waitAll()` ## 使用要点 - 模块对象和它创建的资源对象应保存在全局或实体状态中,不要在每帧重复创建。 - 带 `update(dt)` 的系统应在 `eve_update` 调用;绘制方法应在 `eve_render` 调用。 - 参数约束、默认值和返回类型以对应模块头文件及 `addFunc` 绑定为准;本文 API 快查与当前源码同步生成。 **源码:** [`src/modules/thread/`](../../../src/modules/thread/) **相关测试:** 在 [`test/`](../../../test/) 中搜索 `thread`。 ## 资源解码并发 ResourceManager 使用的 IAsyncWorkExecutor 拥有独立资源池,工作线程数为 `min(8, max(1, hardwareConcurrency))`,限制大型解码的并发内存及 CPU 争用。 普通 `getPool()` 和显式创建的池不受此限制。thread 销毁时先撤销执行器提供者,再等待 资源任务完成;任务不能在工作线程创建模块或操作图形/脚本状态。