Ich habe einen nicht blockierenden Reader in Python implementiert und muss ihn effizienter gestalten.

Der Hintergrund: Ich habe riesige Mengen an Ausgabe, die ich aus einem Unterprozess (angefangen mit Popen ()) lesen und an einen anderen Thread übergeben muss. Das Lesen der Ausgabe aus diesem Unterprozess darf nicht länger als einige ms blockieren (vorzugsweise so wenig Zeit, wie zum Lesen der verfügbaren Bytes erforderlich ist).

Derzeit habe ich eine Utility-Klasse, die einen Dateideskriptor (stdout) und ein Timeout verwendet. Ich select() und readline(1), bis eines von drei Dingen passiert:

  1. Ich habe eine neue Zeile gelesen
  2. Meine Zeitüberschreitung (einige ms) läuft ab
  3. select sagt mir, dass auf diesem Dateideskriptor nichts zu lesen ist.

Dann gebe ich den gepufferten Text an die aufrufende Methode zurück, die damit etwas macht.

Nun zur eigentlichen Frage: Weil ich so viel Ausgabe lese, muss ich dies effizienter gestalten. Ich möchte dies tun, indem ich den Dateideskriptor frage, wie viele Bytes anstehen und dann readline([that many bytes]). Es soll nur Sachen durchgehen, also ist es mir eigentlich egal, wo die Zeilenumbrüche sind oder ob es welche gibt. Kann ich den Dateideskriptor fragen, wie viele Bytes zum Lesen zur Verfügung stehen und wenn ja, wie?

Ich habe etwas gesucht, aber es fällt mir wirklich schwer herauszufinden, wonach ich suchen soll, geschweige denn, wenn es möglich ist.

Schon ein Punkt in die richtige Richtung wäre hilfreich.

Hinweis: Ich entwickle unter Linux, aber das sollte für eine "Pythonic" -Lösung keine Rolle spielen.

7
Matt 19 Nov. 2013 im 21:28

3 Antworten

Beste Antwort

Unter Linux ist os.pipe() nur ein Wrapper um Pipe (2). Beide geben ein Paar Dateideskriptoren zurück. Normalerweise würde man lseek (2) (os.lseek() in Python) verwenden, um den Offset eines Dateidekriptors neu zu positionieren, um die Menge der verfügbaren Daten zu erhalten. Es sind jedoch nicht alle Dateideskriptoren in der Lage zu suchen.

Unter Linux wird bei einem Versuch mit lseek (2) auf einer Pipe ein Fehler zurückgegeben (siehe Handbuchseite) . Das liegt daran, dass eine Pipe mehr oder weniger ein Puffer zwischen einem Produzenten und einem Konsumenten von Daten ist. Die Größe dieses Puffers ist systemabhängig.

Unter Linux hat eine Pipe einen 64-kB-Puffer, sodass Sie so viele Daten wie möglich haben zur Verfügung haben.

Bearbeiten : Wenn Sie die Funktionsweise Ihres Unterprozesses ändern können, können Sie eine Speicherzuordnungsdatei oder einen schönen großen Teil des gemeinsam genutzten Speichers verwenden.

Bearbeiten2 : Verwenden von Abfrageobjekten ist wahrscheinlich schneller als auswählen.

5
Roland Smith 19 Nov. 2013 im 18:14

Diese Frage scheint eine mögliche Lösung zu bieten, erfordert jedoch möglicherweise ein Umrüsten.

Nicht blockierendes Lesen eines Unterprozesses. PIPE in Python

Ansonsten gehe ich davon aus, dass Sie wissen, wie Daten N Bytes gleichzeitig gelesen werden:

all_data = ''
while True:
    data = pipe.read(1024)   # Reads 1024 bytes or to end of pipe
    if not data:
        break
    all_data += data
    # Add your timeout break here
0
Community 23 Mai 2017 im 11:52

Sie können dies herausfinden, indem Sie os.fstat (file_descriptor) aufrufen und die Eigenschaft st_size überprüfen, bei der es sich um die Anzahl der geschriebenen Bytes handelt.

import os
reader_file_descriptor, writer_file_descriptor = os.pipe()
os.write(writer_file_descriptor, b'I am some data')
readable_bytes = os.fstat(writer_file_descriptor).st_size
0
spacether 31 Juli 2018 im 01:18