Skip to content

Electron 多线程池

多线程池

bash

一开始就会在内存中开辟空间,然后线程池会从内存中取出线程,然后执行任务,执行完任务之后,线程会归还到内存中,然后等待下一个任务。

线程最大数最小数

  • Electron 默认最大线程数是 6,最小线程数是 3

使用

创建线程池

  • 在 app.vue 中创建线程池
html
<template>
  <router-view />
</template>

<script>
  import { ipcApiRoute } from "@/api/main";
  import { ipc } from "@/utils/ipcRenderer";
  import { onMounted } from "vue";
  export default {
    name: "App",
    setup() {
      document.getElementById("loadingPage").remove();
      onMounted(() => {
        // 框架规定最多只能6个。最小3个 。这样不会造成用户设备卡死
        console.log("没执行吗");
        ipc.send(ipcApiRoute.createpool, 6);
        ipc.on(ipcApiRoute.createPoolNotice, (event, result) => {
          console.log("********");
          console.log(result);
        });
      });
    },
  };
</script>
<style lang="less"></style>
  • 控制器
js

async createpool(num, event) {
  await Services.get("testjobpool").doCreatePool(num, event);
}
  • Service 层
js
  /**
   * 创建pool
   */
  doCreatePool(num, event) {
    const channel = "controller.testjobpool.createPoolNotice";
    this.myJobPool.create(num).then((pids) => {
      event.reply(`${channel}`, "创建成功,进程号列表" + pids);
    });
  }

使用

  • html
html
<button @click="openjobpool">开启多线程监听(有线程池)</button>
  • js
js
// 监听
created() {
  this.initxcpool();
},
// 函数
initxcpool() {
    ipc.on(ipcApiRoute.testjobpoollistener, this.callbackxcpool);
  },
// 回调
callbackxcpool(event, result) {
  console.log("********");
  console.log(result);
},
// 开启
openjobpool() {
  let params = {
    id: this.id,
    action: "run",
  };
  ipc.send(ipcApiRoute.doJobPool, params);
},
// 关闭
closejobpool() {
  let params = {
    id: this.id,
    action: "close",
  };
  ipc.send(ipcApiRoute.doJobPool, params);
},
  • 控制器
js
async doJobpool(params, event) {
  await Services.get("testjobpool").doJobByPool(
    params.id,
    params.action,
    event
  );
}
  • Service 层
js
/**
 * 通过进程池执行任务
 */
doJobByPool(jobId, action, event) {
  let res = {};
  const channel = "controller.testjob.testjobpoollistener";

  // 异步-执行任务及监听进度 执行job里面的多线程就是路径
  this.myJobPool
    .runPromise("./jobs/testjobpool/testpools", { jobId, action })
    .then((task) => {
      // 监听器名称唯一,否则会出现重复监听。
      // 任务完成时,需要移除监听器,防止内存泄漏
      let eventName = "job-timer-progress-" + jobId;
      task.emitter.on(eventName, (data) => {
        Log.info(
          "[main-process] [ChildPoolJob] timerTask, from TimerJob data:",
          data
        );
        console.log(this.myJobPool.getPids());
        // 发送数据到渲染进程
        event.sender.send(`${channel}`, data);
        // 如果收到任务完成的消息,移除监听器
        if (data.end) {
          task.emitter.removeAllListeners(eventName);
        }
      });
      res.pid = task.pid;
    });

  return res;
}
  • JOB 要执行的
js
/**
 * @Author: jsopy
 * @Date: 2024-09-12 11:46:17
 * @LastEditTime: 2024-09-12 16:42:18
 * @FilePath: /demo1/electron/jobs/testjobpool/testpools.js
 * @Description:
 * @
 */
const Job = require("ee-core/jobs/baseJobClass");
const Loader = require("ee-core/loader");
const Log = require("ee-core/log");
const Ps = require("ee-core/ps");
const { childMessage } = require("ee-core/message");

/**
 * example - Testjobpool
 * @class
 */
class Testjobpool extends Job {
  constructor(params) {
    super();
    this.params = params;
  }

  /**
   * handle()方法是必要的,且会被自动调用
   */
  async handle() {
    Log.info("[child-process] Testjobpool params: ", this.params);
    let timer = null;
    let jobId = this.params.jobId;
    let eventName = "job-timer-progress-" + jobId;
    if (this.params.action == "run") {
      // 计时器任务
      let number = 0;
      timer = setInterval(function () {
        // childMessage.send(eventName, { jobId, number, end: false });
        childMessage.send(eventName, { jobId, number, end: false });
        number++;
      }, 1000);

      // 用 setTimeout 模拟任务运行时长
      setTimeout(() => {
        // 关闭定时器
        clearInterval(timer);

        // 任务结束,重置前端显示
        childMessage.send(eventName, { jobId, number: 0, pid: 0, end: true });

        // 如果是childJob任务,必须调用 Ps.exit() 方法,让进程退出,否则会常驻内存
        // 如果是childPoolJob任务,常驻内存,等待下一个业务
        if (Ps.isChildJob()) {
          Ps.exit();
        }
      }, 10 * 1000);
    }
    if (this.params.action == "close") {
      clearInterval(timer);
      // 任务结束,重置前端显示
      childMessage.send(eventName, { jobId, number: 0, pid: 0, end: true });
    }
  }
}

Testjobpool.toString = () => "[class Testjobpool]";
module.exports = Testjobpool;

总结

流程

页面->控制器->服务层->JOB

注意

  • eventName 服务层和 JOB 里面必须一致
  • JOB 里面要使用 childMessage.send(eventName, { jobId, number, end: false }); 发送数据到主进程
  • JOB 里面要使用 Ps.exit(); 退出进程
  • JOB 里面要使用 Ps.isChildJob(); 判断是否是子进程

控制器代码如下

js
"use strict";

const { Controller } = require("ee-core");
const Services = require("ee-core/services");

/**
 * example
 * @class
 */
class TestjobPoolController extends Controller {
  constructor(ctx) {
    super(ctx);
  }

  /**
   * 所有方法接收两个参数
   * @param args 前端传的参数
   * @param event - ipc通信时才有值。详情见:控制器文档
   */

  /**
   *
   */
  async doJobpool(params, event) {
    await Services.get("testjobpool").doJobByPool(
      params.id,
      params.action,
      event
    );
  }
  async createpool(num, event) {
    await Services.get("testjobpool").doCreatePool(num, event);
  }
}

TestjobPoolController.toString = () => "[class TestjobPoolController]";
module.exports = TestjobPoolController;

Service 层代码如下

js
const { ChildPoolJob } = require("ee-core/jobs");
const { Service } = require("ee-core");
const Log = require("ee-core/log");
/**
 * 示例服务(service层为单例)
 * @class
 */
class Testjobpool extends Service {
  constructor(ctx) {
    super(ctx);

    // 在构造函数中初始化一些变量
    this.myJobPool = new ChildPoolJob();
  }

  /**
   * 创建pool
   */
  doCreatePool(num, event) {
    const channel = "controller.testjobpool.createPoolNotice";
    this.myJobPool.create(num).then((pids) => {
      event.reply(`${channel}`, "创建成功,进程号列表" + pids);
    });
  }

  /**
   * 通过进程池执行任务
   */
  doJobByPool(jobId, action, event) {
    let res = {};
    const channel = "controller.testjob.testjobpoollistener";

    // 异步-执行任务及监听进度
    this.myJobPool
      .runPromise("./jobs/testjobpool/testpools", { jobId, action })
      .then((task) => {
        // 监听器名称唯一,否则会出现重复监听。
        // 任务完成时,需要移除监听器,防止内存泄漏
        let eventName = "job-timer-progress-" + jobId;
        task.emitter.on(eventName, (data) => {
          Log.info(
            "[main-process] [ChildPoolJob] timerTask, from TimerJob data:",
            data
          );
          console.log(this.myJobPool.getPids());
          // 发送数据到渲染进程
          event.sender.send(`${channel}`, data);
          // 如果收到任务完成的消息,移除监听器
          if (data.end) {
            task.emitter.removeAllListeners(eventName);
          }
        });
        res.pid = task.pid;
      });

    return res;
  }
}

Testjobpool.toString = () => "[class Testjobpool]";
module.exports = Testjobpool;

Job 代码如下

  • 路径->jobs->testjobpool>testpools.js
js
/**
 * @Author: jsopy
 * @Date: 2024-09-12 11:46:17
 * @LastEditTime: 2024-09-12 16:42:18
 * @FilePath: /demo1/electron/jobs/testjobpool/testpools.js
 * @Description:
 * @
 */
const Job = require("ee-core/jobs/baseJobClass");
const Loader = require("ee-core/loader");
const Log = require("ee-core/log");
const Ps = require("ee-core/ps");
const { childMessage } = require("ee-core/message");

/**
 * example - Testjobpool
 * @class
 */
class Testjobpool extends Job {
  constructor(params) {
    super();
    this.params = params;
  }

  /**
   * handle()方法是必要的,且会被自动调用
   */
  async handle() {
    Log.info("[child-process] Testjobpool params: ", this.params);
    let timer = null;
    let jobId = this.params.jobId;
    let eventName = "job-timer-progress-" + jobId;
    if (this.params.action == "run") {
      // 计时器任务
      let number = 0;
      timer = setInterval(function () {
        // childMessage.send(eventName, { jobId, number, end: false });
        childMessage.send(eventName, { jobId, number, end: false });
        number++;
      }, 1000);

      // 用 setTimeout 模拟任务运行时长
      setTimeout(() => {
        // 关闭定时器
        clearInterval(timer);

        // 任务结束,重置前端显示
        childMessage.send(eventName, { jobId, number: 0, pid: 0, end: true });

        // 如果是childJob任务,必须调用 Ps.exit() 方法,让进程退出,否则会常驻内存
        // 如果是childPoolJob任务,常驻内存,等待下一个业务
        if (Ps.isChildJob()) {
          Ps.exit();
        }
      }, 10 * 1000);
    }
    if (this.params.action == "close") {
      clearInterval(timer);
      // 任务结束,重置前端显示
      childMessage.send(eventName, { jobId, number: 0, pid: 0, end: true });
    }
  }
}

Testjobpool.toString = () => "[class Testjobpool]";
module.exports = Testjobpool;

这里特别注意