欢迎光临散文网 会员登陆 & 注册

Unity Webgl Jslib + Web Worker的方式实现多线程

2022-09-18 00:31 作者:宇佐美艾露  | 我要投稿

        最近做项目遇到了一个需求,进入场景可能需要加载几百张外部图片,最开始使用协程在主线程中加载,发现加载很慢很卡,严重影响使用。但因为unity webgl是不支持C#的多线程的,在前端唯一能实现多线程的手段只有一个——Web Worker(https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API)。那么问题的解决思路就很明了了:如何在unity中调用JS的方法,开启Web Worker,并在Web Worker线程获得结果后能够传递到主线程回调C#的方法。

        首先,是C#调用JS创建Worker。这个unity官方文档里说的就很明白,只要新建一个Jslib文件,在里面实现要调用的方法,然后在C#里面声明相同的引用方法就好了:

具体参数可以根据自己业务需求确定。

        上面代码能够创建Worker线程,但是问题来了onmessage方法的执行结果必定是异步返回的,那么什么方式才能在得到结果时候告诉unity的C#端呢?其实一般编译成的wasm的项目,调用的时候会有用dyncall library。我们只要知道回调的方法的引用或者说指针,就可以回调。具体代码如下:

        上面的代码是假设说我们worker线程返回了一个fileName和文件的ArrayBuffer,我们使用_malloc手动开辟内存,把对应的值塞进去并获得对应的指针。然后调用Module.dynCall_viii来执行PostMsg传过来callback引用。

        Module.dynCall后边的viii指的是回调函数的签名,第一个v表示void,意思是callback是没有返回值的。i表示参数类型是int。因为我们获得的指针类型都是int。还有其他的选项比如f表示float,d表示double等等。string的指针会自动转换为C#的string类型,但是ArrayBuffer需要使用IntPtr指针转换到C#中使用。

        以上就是主要的在jslib中开启Web Worker多线程并回调C#的方式。但是还有一些瑕疵。    

        1.每次调用都会开启一个woker线程,但是CPU核心线程是有限的,太多的线程反而会导致线程之间上下文切换造成额外的性能消耗。推荐使用navigator.hardwareConcurrency获取客户端机器的线程数来创建一个Worker池。我的做法是用一个worker生成另外的几个worker来创建并维护一个woker池子,并把这个worker注册到jslib中。

        

        

        Google Chrome 17 与 Firefox 18 包含另一种性能更高的方法来将特定类型的对象 (可转让对象 transferable objects) 传递给一个 worker/从 worker 传回。可转让对象从一个上下文转移到另一个上下文而不会经过任何拷贝操作,类似于指针,因此拥有很高的性能。这里我在workerPool worker和业务worker的postMessage中都使用了可转让对象。对于加载文件类型的数据,性能提升很大。

        以上,如果有问题或者建议欢迎在评论区指正。爱你哟~

Unity Webgl Jslib + Web Worker的方式实现多线程的评论 (共 条)

分享到微博请遵守国家法律