Maison >développement back-end >Tutoriel Python >Pourquoi mon Python `subprocess.readline` se bloque-t-il lors de la lecture d'un programme C écrivant en continu ?

Pourquoi mon Python `subprocess.readline` se bloque-t-il lors de la lecture d'un programme C écrivant en continu ?

Linda Hamilton
Linda Hamiltonoriginal
2024-11-19 16:09:03596parcourir

Why Does My Python `subprocess.readline` Hang When Reading from a Continuously Writing C Program?

Readline du sous-processus Python se bloque à "for line in iter"

Problème

Lors de la tentative de lecture de la sortie d'un programme C, à savoir "2000 ", à partir d'un script Python utilisant un sous-processus, le script se bloque à la ligne "for line in iter(process.stdout.readline, '')". Cela se produit bien que le programme C imprime continuellement "2000" et n'atteigne jamais un état final.

Solution

Le problème provient d'un problème de mise en mémoire tampon de bloc. Par défaut, la sortie standard des programmes C exécutés via des canaux (par exemple, lors de l'utilisation de subprocess.Popen) est mise en mémoire tampon. Cela signifie que les données ne sont pas vidées dans le canal tant que le tampon n'est pas plein ou explicitement vidé.

Option 1 : modifier directement le programme C

Pour résoudre le problème, vous peut forcer la mise en mémoire tampon de la sortie standard dans le programme C :

#include <stdio.h>

int main() {
    setvbuf(stdout, (char *) NULL, _IOLBF, 0); // Make line buffered stdout
    while (1) {
        printf("2000\n");
        sleep(1);
    }
    return 0;
}

Option 2 : Utiliser l'utilitaire stdbuf

Vous pouvez également utiliser l'utilitaire stdbuf pour modifier le type de mise en mémoire tampon sans modifier la source du programme C :

from subprocess import Popen, PIPE

process = Popen(["stdbuf", "-oL", "./main"], stdout=PIPE, bufsize=1)
for line in iter(process.stdout.readline, b''):
    print(line)
process.communicate()

Option 3 : Utiliser un pseudo-TTY

Une autre approche consiste à utiliser un pseudo-TTY pour tromper le programme C en pensant qu'il fonctionne de manière interactive :

import os
import pty
import sys
from select import select
from subprocess import Popen, STDOUT

master_fd, slave_fd = pty.openpty()
process = Popen("./main", stdin=slave_fd, stdout=slave_fd, stderr=STDOUT,
                bufsize=0, close_fds=True)
timeout = .1
with os.fdopen(master_fd, 'r+b', 0) as master:
    input_fds = [master, sys.stdin]
    while True:
        fds = select(input_fds, [], [], timeout)[0]
        # Handle subprocess output and user input here

Option 4 : Utiliser pexpect

Pexpect offre une interface de niveau supérieur qui simplifie la gestion des pseudo-ATS :

import pexpect

child = pexpect.spawn("./.main")
for line in child:
    print(line)
child.close()

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn