# worker任务

# 简要介绍

在小游戏框架的 worker 框架中,想要编写在 worker 中运行的代码,只能通过 worker 任务的形式。

在编写完 worker 任务后,任务由小游戏框架自动调度,每帧会执行一次,并在帧末与主线程进行数据交换。

# worker任务类

所有 worker 任务都需要继承Job类。

# ▌Job基类的属性和方法

成员 类型 注释
data object 引擎内创建的交互数据块
init abstract ( ) => void 初始化,worker内创建任务时调用
update abstract ( ) => void 在 update 里实现每帧的真实工作;
可通过访问 this.data 来获取交互数据块
onDispose abstract ( ) => void 任务中止时的回调。

自己实现任务类的时候,只要实现以上四个成员就行了:

import { Job } from "engine-worker-env";

class MyJob extends Job {
    public data: object;
    public init() {
        ...
    }
    public update() {
        // real work
    }
    public onDispose() {
        ...
    }
}

# worker任务的文件位置

worker任务只能放置于 worker 文件夹下。

如果还未创建 worker 文件夹,可以在工具内右键某个文件夹,选择设置为 worker 文件夹。创建完毕之后,请勿更改自动生成的文件。

worker任务的路径定义为,worker任务类相对于 worker 文件夹的路径(去除后缀名)。

假如 worker 文件夹的路径为游戏根目录/assets/worker/,worker任务的绝对路径为游戏根目录/assets/worker/jobs/MyJob.ts

那么 MyJob 的路径为jobs/MyJob。可以用该路径来创建 worker 任务。

# worker任务的环境

worker任务里的代码只能访问 worker 文件夹下的内容,并且和游戏主线程是两个互相独立的 js 环境。

也就是说:

  1. 不能访问游戏主线程的环境,拿不到enginegame等全局变量,也拿不到任何资源
  2. 不能 import worker文件夹外的脚本;
  3. 不能使用 worker 文件夹外的 npm 包(要用的话只能在 worker 文件夹下再npm install一个)。

所以在 worker 环境里不能访问游戏内的内容。想要访问的话,只能通过帧末和主线程交换数据,让主线程把数据序列化填在共享内存中。

# 交互数据块

在创建 worker 任务时,需要传入一个用于交互的数据块(jsObject)。

这个数据块在发到至 worker 的过程中,会经历序列化和反序列化的过程,所以主线程和 worker 持有的数据块并不是同一份,而是数据的深拷贝。

所以两边想要进行数据交换,只能通过共享内存的方式。小游戏框架内做了工作,来确保共享内存对象在发送至 worker 后,和主线程访问的仍是同一份内存。

// 创建共享内存并发送
const sab = game.workerSystem.createSharedArrayBuffer(32);
const data = {
    buffer: sab
};
const job = game.workerSystem.createJob("jobs/MyJob", data);

// 修改共享内存数据
const view = new Uint8Array(sab.buffer);
view[0] = 1;

# worker任务的状态

worker任务内部是一个状态机,大致流程为:

在主线程创建任务并启动之后,worker环境可能会在1~3帧之后才收到,所以主线程内的任务回调可能会空跑几帧。关于 worker 有没有进行第一次任务,可以通过 worker 任务的状态来判断,如果状态为running,则说明至少在 worker 里跑了一帧了。

# 注意事项

由于每个 job 拥有单独的通信开销,所以尽量不要建太多job