# 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