Skip to content

WASM 异步与游戏时间 ​

WasiCore 游戏脚本运行在单线程 WASM 中。async / await 可以等待框架回调和异步请求,但不会让计算自动转到后台线程。

不要调用线程池和系统计时器 ​

错误写法原因与替代方式
Task.Run(...)、ThreadPool、new Thread(...)、Parallel.ForEach(...)游戏脚本没有可用的线程池;直接调用异步方法,或把计算拆到多个 Tick
Task.Delay(...)、依赖 .NET 系统计时器的 API使用 Game.Delay(...) 按游戏时间等待
TaskCreationOptions.RunContinuationsAsynchronously会把后续执行投递到 ThreadPool,在 .NET WASI 中可能抛出 PlatformNotSupportedException;TaskCompletionSource<T> 使用默认构造函数
等待未完成任务的 .Result / .Wait()阻塞驱动任务完成的游戏线程;改用 await
csharp
await Game.Delay(TimeSpan.FromSeconds(1));
await Game.NextTick();

// 把回调转换为可等待的 Task 时,可使用默认构造函数。
var completion = new TaskCompletionSource<bool>();

Game.NextTick() 等待下一个游戏 Tick,不能据此假定固定的 60 FPS。

等待期间,对象和业务状态可能变化 ​

await 让出执行后,单位可能死亡、控件可能被销毁、当前轮次也可能已经取消。恢复执行时重新检查,而不只在等待前检查。

下面是客户端控件延迟显示的最小例子:

csharp
#if CLIENT
static async Task ShowLater(GameUI.Control.Control control)
{
    await Game.Delay(TimeSpan.FromSeconds(1));
    if (!Game.IsActive || control.IsDisposed)
    {
        return;
    }

    control.Visible = true;
}
#endif

检查游戏和对象仍有效,只解决生命周期问题。技能被打断、回合切换等业务取消,还需要项目自己的取消条件或令牌;Game.Delay 和 Game.NextTick 接受 CancellationToken。不要用 CancellationTokenSource.CancelAfter 再引入系统计时器。

游戏时间与超时 ​

Game.Delay 受游戏 Tick、暂停和倍速影响。游戏暂停时,基于它设置的超时也会暂停;需要真实时间超时的网络操作应查对应 API 的超时语义。等待超时或取消等待,不代表远端事务已撤销。

Game.Delay 返回框架的可等待对象,继承 GameCore.TaskSystem.Task,并非 System.Threading.Tasks.Task。不要直接把它传给要求 Task 的 Task.WhenAny;确需组合时,可先用异步方法封装,且必须处理未完成操作的后续结果。

异步异常 ​

能等待结果时使用 await 让异常传回调用方。确需 _ = SomeAsyncMethod() 时,在该方法内部记录异常,保留异常对象和操作上下文;丢弃任务不会自动处理异常。不要用 Task.Run 包住它。

相关文档:生命周期、日志系统。