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

GLib调用Python脚本无法检测STDOUT输出问题排查

问题:GLib监听Python脚本输出时回调未触发的原因及解决办法

核心原因

你的问题根源在于Python标准输出的缓冲机制:

  • 当Python的stdout连接终端时,默认是行缓冲,print后会立刻刷新;但当它连接到管道(GLib通过g_spawn_async_with_pipes创建的管道属于这种情况)时,Python会切换为全缓冲模式。
  • 这意味着Python要攒够足够多的输出数据(通常是4KB或8KB)才会把内容写到管道里,而你的脚本每次输出的内容不足缓冲阈值,加上每次sleep5秒,所以GLib的G_IO_IN事件一直没触发,直到进程结束时缓冲区才被强制刷新。

另外,你的emit回调返回FALSE,即使触发一次也会立刻移除监听,但这不是当前的主要问题。

解决办法

方法一:修改Python脚本禁用缓冲

有三种方式可以让Python立刻输出内容:

  1. 给print加flush参数:
import time
for x in range(10):
    print("Eat imagine you chiefly few end ferrars compass. Be visitor am ferrars inquiry. Latter law remark two lively thrown. Spot set they know rest its. Raptures law diverted believed jennings consider children the see. Had invited beloved carried the colonel. Occasional principles discretion it as he unpleasing boisterous. She sing dear now son half.", flush=True)
    time.sleep(5)
  1. 启动Python时加-u参数:
    修改C代码里的命令行,把/usr/bin/python3 /home/joe/slow_output.py改成/usr/bin/python3 -u /home/joe/slow_output.py,-u参数会强制Python的stdout和stderr使用无缓冲模式。
  2. 在脚本开头修改stdout缓冲:
import sys
import time
# 强制设置为行缓冲
sys.stdout = open(sys.stdout.fileno(), 'w', buffering=1)
for x in range(10):
    print("Eat imagine you chiefly few end ferrars compass. Be visitor am ferrars inquiry. Latter law remark two lively thrown. Spot set they know rest its. Raptures law diverted believed jennings consider children the see. Had invited beloved carried the colonel. Occasional principles discretion it as he unpleasing boisterous. She sing dear now son half.")
    time.sleep(5)

方法二:完善C端的回调逻辑

即使解决了缓冲问题,你当前的emit回调还有两个问题:

  1. 返回FALSE会导致监听被移除,只能触发一次;
  2. 如果不读取管道里的数据,当管道缓冲区满时,Python进程会被阻塞住,无法继续执行。

修改后的回调示例:

gboolean emit(GIOChannel *source, GIOCondition condition, gpointer data) {
    gchar *buf = NULL;
    gsize bytes_read = 0;
    GError *err = NULL;

    // 读取一行输出
    GIOStatus status = g_io_channel_read_line(source, &buf, NULL, NULL, &err);
    if (status == G_IO_STATUS_NORMAL) {
        g_print("Received output: %s", buf);
        g_free(buf);
    } else if (status == G_IO_STATUS_EOF) {
        g_print("Child output ended\n");
        return FALSE; // 结束监听
    } else {
        g_print("Error reading output: %s\n", err->message);
        g_error_free(err);
        return FALSE;
    }
    return TRUE; // 保持监听,等待下一次输出
}

额外注意事项

  • 记得检查g_spawn_async_with_pipes的返回值和error指针,排查是否有进程启动失败的情况;
  • 使用g_io_channel_unix_new后,GLib会接管文件描述符的生命周期,不需要手动关闭,但如果不再使用,也可以调用g_io_channel_unref(channel)释放资源。

内容的提问来源于stack exchange,提问作者9-Pin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 07:16:10