#web-worker #worker-thread #thread #async-task #async-executor #async

wasm-futures-executor

基于wasm web workers的异步任务执行器

4个版本

0.2.0 2022年3月23日
0.1.2 2021年9月10日
0.1.1 2021年9月10日
0.1.0 2021年9月8日

#368WebAssembly


2 crate 中使用

Apache-2.0 OR MIT

20KB
197

wasm-futures-executor

License Cargo Documentation

这个crate提供了一个与futures_executor::ThreadPool相同的API的异步任务执行器,针对Web浏览器环境。不是通过std::thread创建线程,而是创建web workers。这个crate努力使这个过程尽可能无缝和容易。

示例用法

use futures::channel::mpsc;
use futures::StreamExt;
use js_sys::Promise;
use wasm_bindgen::prelude::*;
use wasm_futures_executor::ThreadPool;

#[wasm_bindgen]
pub async fn start() -> Result<JsValue, JsValue> {
    let pool = ThreadPool::max_threads().await?;
    let (tx, mut rx) = mpsc::channel(10);
    for i in 0..20 {
        let mut tx_c = tx.clone();
        pool.spawn_ok(async move {
            tx_c.start_send(i * i).unwrap();
        });
    }
    drop(tx);
    let mut i = 0;
    while let Some(x) = rx.next().await {
        i += x;
    }
    Ok(i.into())
}

.. 使用它

import init, { start } from './sample.js';

async function run() {
  await init();

  const res = await start();
  console.log("result", res);
}
run();

使用以下命令构建你的项目

RUSTFLAGS='-C target-feature=+atomics,+bulk-memory,+mutable-globals' \
  cargo +nightly build --target wasm32-unknown-unknown --release -Z build-std=std,panic_abort

wasm-bindgen \
  ./target/wasm32-unknown-unknown/release/sample.wasm \
  --out-dir . \
  --target web \
  --weak-refs

.. 或者如果你想要使用wasm-pack build -t web,请确保设置夜间工具链和正确的RUSTFLAGS(例如,通过创建rust-toolchain.toml.cargo/config文件,就像这个仓库中那样)。

请查看示例,其中包含一个完整的端到端示例项目,不使用打包器,以及使用Webpack 5的示例-webpack

注意:这个crate需要使用web目标的wasm-bindgen/wasm-pack。鉴于ES模块的广泛标准化,这应该大部分没问题。

工作原理

类似于futures-executor提供的异步执行器,在ThreadPool实例化时,会启动多个worker "线程"。每个线程都是一个web worker,它加载一些js粘合代码。粘合代码作为js片段提供,该片段从wasm-bindgen生成的js粘合代码中链接。这样,它对任何打包器都是透明的。每个web worker使用以下参数构造

  1. WebAssembly模块初始化及其共享内存(《SharedArrayBuffer》)。
  2. 第三个是一个指向某些共享状态的指针,包括一个通道,异步任务通过该通道传递。为此,库提供了 worker_entry_point 函数。

一旦 ThreadPool 被丢弃,所有通道都将关闭,并且 Web 工作线程将被终止。

遗憾的是,这需要每晚的编译器,因为 Rust 的标准库需要重新编译以包含以下不稳定功能

  • atomics:(Rust 功能)支持 wasm 原子操作,请参阅 https://github.com/rust-lang/rust/issues/77839
  • bulk-memory:(LLVM 功能)生成原子指令、共享内存、被动段等。
  • mutable-globals:(LLVM 功能)

注意:当工作线程被销毁时,可能会泄漏一些内存(例如线程局部存储或线程的栈)。我建议向 --weak-ref 选项传递 wasm-bindgen,以便让 wasm-bindgen 根据以下 WeakRef 建议创建用户定义的终结器

总的来说,在 Rust 中实现 wasm 线程似乎失去了一些势头 ..

浏览器支持

通过 SharedArrayBuffer 共享内存很快(从 Chrome 92 开始)将需要设置正确的标头以标记网站为“跨源隔离”,这意味着需要与主文档一起发送以下标头

Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin

更多信息请参阅 此链接

在 Web 工作线程中加载模块目前仅在 Chromium 中受支持。Firefox 似乎终于取得了一些 进展。作为一种解决方案,您可以包含一个 workers-polyfill

如果您针对的是旧版浏览器,您可能需要进行一些功能检测,并且优雅地回退--但这超出了本文档的范围。

这值得吗?

在线程边界发送和生成未来存在明显的开销。这对于长期任务来说最有意义(例如,查看 阶乘 演示,性能提升了大约 3 倍)。像往常一样,请确保对您的用例进行性能分析。

许可证

根据您选择的以下许可证之一

供您选择。

贡献

除非您明确声明,否则您提交的任何有意包含在本作品中的贡献(根据 Apache-2.0 许可证定义),都将根据上述许可进行双重许可,而无需任何其他条款或条件。

依赖项

~9–18MB
~242K SLoC