解决webassembly pthread 子线程调用主线程js问题
背景:
web端项目做了一段时间后,我们需求是加载工程是异步的,主线程会调用wasm方法,wasm内部用pthread创建出来线程,然后在这个线程里边处理任务,处理完成后,需要通知主线程加载完成了,但是这个通知怎么实现,花了一些时间。下边就是之前整理的方案
具体调用逻辑如下图所示:
我们知道web端work相关通信是 通过postmessage,onmessage实现的,接下来尝试各种方案
方案一:
Webassembly 我们用c或者c++创建的线程,编译器最终把它转成work,也就是说我们能不能通过work 之间通信解决这个问题呢,我想是可以的。但是十分不方便,因为编译在编译后生成浇水js代码,以及创建线程需要work的浇水代码,我们要改动浇水里边的通信方式, 在次编译后又给覆盖了,极其不方便。
从上图也可以看出来,改好后,重新编译代码又给覆盖了。
方案二
js MessageChannel 也可以实现work之间通信。
Instance properties
MessageChannel.port1 Read only
Returns port1 of the channel.
MessageChannel.port2 Read only
Returns port2 of the channel.
具体用法网上有很多,work 持有MessageChange port2, 主线程池游port1,两者可以通过port的postMessage与onMessage进行通信,这种方法在js层面比较方便,但是我们这个要求是wasm里边的work,
主线程怎么把port给传到子线程work中,都比较费力,显然也不是好的方案
方案三
主线程轮训的方式,也就是我们主线程调用LoadProject后,我们返回一个uniqueid, 主线程设置setInterval,开启个定时任务,每隔一定时间查询下uniqueid 代码的任务是否完成,原理是uniqueid关联一个future,可以通过future去实现任务是否完成的查询。
很显然这种方式是可以的,但是需要额外的浪费主线程的一点点性能,
方案四
有没有直接通过调用一个c方法直接把方法抛到主线程去执行的呢,这也是我所期望的。为了实现这个目标我约的emscripten 里边的源码,找到了一些蛛丝马迹,觉得应该是有接口直接调用的。
在test_pthread_proxying.c这个文件中看到了,emscripten_proxy_async 这个方法,接着我就去看下这个方法对应的实现,
C++ |
通过上边源码也可以看的出来target_thread,是目标线程的id,可以通过pthread_self()获取, func与args需要执行函数,与args参数,
首先会把根据target_thread 把所在线程的tasks(任务队列取出来),然后把我们的func加进去, 所在线程会调用emscripten_proxy_execute_queue 来执行任务队列,因此这种方式满足了我们的需求。