侧边栏壁纸
  • 累计撰写 218 篇文章
  • 累计创建 59 个标签
  • 累计收到 5 条评论

electron中使用piscina线程池

barwe
2022-08-19 / 0 评论 / 0 点赞 / 1,405 阅读 / 3,002 字
温馨提示:
本文最后更新于 2023-06-07,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

技术栈:Electron + Vite2 + Vue3 + TypeScript,线程池用的 Piscina。

一个简单的 Piscina 线程池使用例子

使用 Piscina 线程池至少需要两个文件:

  • 创建 Piscina 实例、分发任务的主线程文件,可以用 TypeScript 写
  • 执行任务的 Worker 文件,暂时用 mjs 写的,方便,没有 bug
import Piscina from 'piscina'
const piscina = new Piscina({ filename: 'listdir.mjs' })
const result = await piscina.run({a: 2, b: 3})

在上面 piscina.ts 同一目录下新建 Worker:

export default function ({a, b}) {
    return a + b
}

在实际项目中,应用启动时的目录一般是项目根目录,这样构建 Piscina 实例时的filename实际上是不存在的。

开发时使用 Piscina 线程池

需要将 filename 改成相对于项目根目录的路径,我们可以将 Worker 放在 electron/main/worker 目录下:

electron/main/worker/worker.mjs

在启动开发服务器时,我们需要将这个文件单独复制到 Electron 打包目录中:

dist/electron/main/worker.mjs

因此需要安装一个插件 rollup-plugin-copy

ni -D rollup-plugin-copy

然后配置插件:

import copy from 'rollup-plugin-copy'

copy({
    targets: [
        {
          src: 'electron/main/worker/*.mjs',
          dest: 'dist/electron/main/',
        },
    ]
})

一般开发服务器热刷新时都会先删除 dist 目录,这里有一个问题是,如果使用下面的命令将整个 dist 目录删除:

rmSync('dist', { recursive: true, force: true })

因为 dest 目录不存在,复制工作将不能完成。因此我们需要保留这个目录,并且将其他内容全部删掉:

import { rmSync, readdirSync } from 'fs'
const distDir = 'dist/electron'
readdirSync(distDir).forEach(k => {
    // "dist/electron/main" required for copying workers
    if (k === 'main') {
        rmSync(join(distDir, k, '*'), { recursive: true, force: true })
    } else {
        rmSync(join(distDir, k), { recursive: true, force: true })
    }
})

所以一个完整的插件配置是:

import { defineConfig } from 'vite'
import { rmSync, readdirSync } from 'fs'
import copy from 'rollup-plugin-copy'

const distDir = 'dist/electron'
readdirSync(distDir).forEach(k => {
    // "dist/electron/main" required for copying workers
    if (k === 'main') {
        rmSync(join(distDir, k, '*'), { recursive: true, force: true })
    } else {
        rmSync(join(distDir, k), { recursive: true, force: true })
    }
})

export default defineConfig({
    plugins: [
        copy({
            targets: [
                {
                    src: 'electron/main/worker/*.mjs',
                    dest: 'dist/electron/main/',
                },
            ]
        })
    ]
})

这样我们就能看到 Electron 打包后目录结构:

  • dist
    • electron
      • main
        • index.js
        • worker.mjs
      • preload
        • index.js

然后将这个 dist/electron/main/worker.mjs 路径传递给 Piscina:

import Piscina from 'piscina'
import { join, resolve } from 'path'

const getWorkerPath = (worker: string) => {
    if (process.env.NODE_ENV === 'development') {
        return resolve(join('dist', 'electron', 'main', worker))
    }
}

const piscina = new Piscina({
    filename: getWorkerPath('listdir.mjs'),
})

const result = await piscina.run({a: 2, b: 3})

上述方案解决了开发服务器下使用 Piscina 的问题,那么生产环境呢?

生产时使用 Piscina 线程池

dist 目录是用来存放打包后资源的地方,Electron 打包分为两部分:

  • 主线程打包,这个由 vite 每次热更新时自动打包完成,位置是 dist/electron
  • 页面打包,这部分是纯前端的打包,位置是 dist/

执行应用打包(nr build)时分为三步:

  • vue-tsc --noEmit 检查 TypeScript
  • vite build 打包前端代码
  • electron-builder 打包整个应用

electron-builder 默认将整个 dist 下的内容打包到 asar 文件中,这就导致我们的 worker.mjs 也被放到了 app.asar 文件中,那么我们就不能在文件系统中访问到 worker.mjs 了。为了解决这个问题,我们可以

  1. 禁用 asar,当然官方不推荐这种做法

  2. 设置不打包指定文件,具体的

    {
        "asar": true,
        "asarUnpack": [
            "./dist/electron/main/worker.mjs"
        ]
    }
    

如果使用第一种方法,worker.mjs 的位置是 resources/app/dist/electron/main/worker.mjs

如果使用第二种方法,worker.mjs 的位置是 resources/app.asar.unpacked/dist/electron/main/worker.mjs

同时修改 Worker 路径搜索函数:

const getWorkerPath = (worker: string) => {
  let p = join('dist', 'electron', 'main', worker)
  if (process.env.NODE_ENV !== 'development') {
    // disable asar when building electron
    if (existsSync(p)) p = join('resources', 'app', p)
    // enable asar when building electron
    else p = join('resources', 'app.asar.unpacked', p)
  }
  return resolve(p)
}

over

0

评论区