You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在GJS中实现并发调用aosd_cat并管理取消句柄

解决方案:GJS中并行调用aosd_cat并管理进程句柄

1. 核心思路

GJS没有ThreadPoolExecutor,但可以通过GLib.Subprocess的异步API实现并行进程启动——每个aosd_cat实例独立初始化启动,无需等待前一个进程结束。同时将每个Subprocess实例作为"cancelHandle"存入数组,后续可批量终止所有进程。

2. 实现代码(TypeScript转GJS)

先定义存储进程的全局数组,再封装启动和批量取消的核心函数:

import GLib from '@girs/glib-2.0';

// 存储所有aosd_cat进程句柄,用于后续批量取消
const aosdProcesses: GLib.Subprocess[] = [];

/**
 * 异步启动单个aosd_cat进程,向其stdin传递显示文本
 * @param text 无障碍元素的显示文本
 * @param aosdArgs aosd_cat的命令行参数(如位置、字体大小等)
 */
async function launchAosdCat(text: string, aosdArgs: string[] = []) {
    // 配置子进程:启用stdin管道,静默stdout/stderr避免干扰
    const subprocess = new GLib.Subprocess({
        argv: ['aosd_cat', ...aosdArgs],
        flags: GLib.SubprocessFlags.STDIN_PIPE | GLib.SubprocessFlags.STDOUT_SILENCE | GLib.SubprocessFlags.STDERR_SILENCE
    });

    // 关键步骤:初始化启动进程(之前漏这一步会导致进程无法运行)
    subprocess.init(null);

    // 保存进程句柄到数组
    aosdProcesses.push(subprocess);

    try {
        // 异步向stdin写入文本,写完后关闭管道
        const stdin = subprocess.get_stdin_pipe();
        await new Promise<void>((resolve, reject) => {
            stdin.write_all(text, null, (stream, result) => {
                try {
                    stream.write_all_finish(result);
                    stream.close(null);
                    resolve();
                } catch (e) {
                    reject(e);
                }
            });
        });

        // 监听进程结束,自动从数组移除已完成的进程,避免无效句柄堆积
        subprocess.wait_async(null, (proc, result) => {
            proc.wait_finish(result);
            const index = aosdProcesses.indexOf(proc);
            if (index !== -1) aosdProcesses.splice(index, 1);
        });
    } catch (err) {
        console.error('aosd_cat启动失败:', err);
        // 启动失败时移除无效进程句柄
        const index = aosdProcesses.indexOf(subprocess);
        if (index !== -1) aosdProcesses.splice(index, 1);
    }
}

/**
 * 批量终止所有正在运行的aosd_cat进程
 */
function cancelAllAosd() {
    aosdProcesses.forEach(proc => {
        // 仅终止未退出/未被信号终止的进程
        if (!proc.get_if_exited() && !proc.get_if_signaled()) {
            proc.terminate();
            // 若terminate无效,可替换为强制终止:proc.kill();
        }
    });
    // 清空进程数组
    aosdProcesses.length = 0;
}

3. 关键注意事项

  • 必须调用subprocess.init():这是很多开发者遗漏的步骤,缺少它会导致进程无法启动,也是你之前用communicate_async()无响应的核心原因。
  • 异步写入stdin:用write_all_async而非同步写入,避免阻塞GJS主循环。
  • 自动清理无效进程:在wait_async回调中移除已结束的进程,防止数组积累无效句柄。
  • 批量终止逻辑:先检查进程状态,再调用终止方法,避免对已结束进程做无效操作。

4. 使用示例

遍历无障碍元素并行启动aosd_cat,GUI更新时调用批量取消:

// 假设你有一个无障碍元素数组
const accessibleElements = ['按钮:确定', '输入框:用户名', '标签:密码'];

// 并行启动所有aosd_cat,每个元素设置不同位置避免重叠
accessibleElements.forEach((elem, idx) => {
    const aosdArgs = ['-x', `${150 + idx * 200}`, '-y', '300', '-s', '16'];
    launchAosdCat(elem, aosdArgs);
});

// GUI更新时调用批量取消
// cancelAllAosd();

5. 关于communicate_async()的说明

communicate_async()需要先调用subprocess.init()才能生效,它适合一次性处理stdin/stdout/stderr的简单场景。但如果需要更灵活的stdin写入控制(比如分批次传递内容),直接操作管道会更可控。


内容的提问来源于stack exchange,提问作者Coder748

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 17:43:16