2009-05-04 6 views
14

Lassen Sie uns sagen, dass ich eine Linie von einem Socket lesen möchten, die Standard-socket Modul:Python-Buchsen Pufferung

def read_line(s): 
    ret = '' 

    while True: 
     c = s.recv(1) 

     if c == '\n' or c == '': 
      break 
     else: 
      ret += c 

    return ret 

Was in s.recv(1) genau passiert? Wird es jedes Mal einen Systemaufruf auslösen? Ich denke, ich soll eine gewissen Puffer hinzufügen, trotzdem:

Für beste Übereinstimmung mit Hardware und Netzwerk Realitäten, der Wert von bufsize sollte eine relativ geringe Potenz von 2 sein, zum Beispiel, 4096.

http://docs.python.org/library/socket.html#socket.socket.recv

Aber es scheint nicht einfach zu schreiben effiziente und thread-sichere Pufferung. Was passiert, wenn ich file.readline() verwende?

# does this work well, is it efficiently buffered? 
s.makefile().readline() 
+0

"Gibt es jedes Mal einen Systemaufruf?" Warum ist das wichtig? –

+6

Weil Systemaufrufe langsam sind. Es ist besser, einen großen Datenblock (falls verfügbar) zu holen und dann zu verarbeiten. Jetzt weiß ich, dass Python nicht besonders schnell ist, und vielleicht ist das nicht wirklich wichtig. Aber die Dokumentation sagt, dass es sowieso besser ist, große Teile zu lesen. –

+7

Beachten Sie, dass das Erstellen eines Strings mit '+ =' ein No-No ist, da es potentiell quadratisch ist, während das Erstellen einer Liste mit append die Verwendung von 'str.join' am Ende immer linear ist. –

Antwort

18

Das ist recv() Anruffunktion direkt durch Aufruf der C-Bibliothek behandelt.

Es wird blockiert warten auf den Socket Daten zu haben. In Wirklichkeit wird es nur den recv() Systemaufruf blockieren lassen.

file.readline() ist eine effiziente gepufferte Implementierung. Es ist nicht threadsicher, weil es annimmt, dass es der einzige ist, der die Datei liest. (Zum Beispiel durch den kommenden Eingang Pufferung.)

Wenn Sie die Datei-Objekt verwenden, read() jedes Mal mit einem positiven Argument aufgerufen wird, der zugrunde liegende Code wird recv() nur die Menge der Daten angefordert, es sei denn, es bereits gepuffert wird.

Es wäre gepuffert werden, wenn:

  • Sie Readline-() aufgerufen hatte, die einen vollen Puffer

  • das Ende der Zeile vor dem Ende des Puffers war liest

So lassen Sie Daten im Puffer. Ansonsten wird der Puffer in der Regel nicht überfüllt.

Das Ziel der Frage ist nicht klar. Wenn Sie vor dem Lesen prüfen müssen, ob Daten verfügbar sind, können Sie select() oder den Socket mit s.setblocking(False) in den nicht blockierenden Modus versetzen. Dann werden die Lesevorgänge leer und nicht blockiert, wenn keine Daten warten.

Lesen Sie eine Datei oder einen Socket mit mehreren Threads? Ich würde einen einzelnen Arbeiter damit beauftragen, den Sockel zu lesen und empfangene Gegenstände in eine Warteschlange für die Behandlung durch andere Fäden zu geben.

Vorschlagen Beratung Python Socket Module source und C Source that makes the system calls.

+0

Ich weiß nicht wirklich warum ich nach Thread-Sicherheit gefragt habe, ich brauche es nicht in meinem aktuellen Projekt. Eigentlich möchte ich ein Java-Programm in Python umschreiben. In Java ist es einfach, gepuffertes Lesen zu bekommen, und ich frage mich, ob Pythons Socket-Modul die gleiche Pufferung bietet (in der Tat frage ich mich, warum jemand nicht puffern möchte und stattdessen direkt Systemaufrufe aufruft). –

+0

reales() ist nicht Echtzeit. also ist es nutzlos für interaktive TCP-Dienste wie SMTP, Readline scheint jedoch zu funktionieren. – Jasen

22

Wenn Sie mit der Leistung betroffen sind und die Kontrolle der Steckdose vollständig (Sie werden nicht in eine Bibliothek zum Beispiel vorbei), dann versuchen in Python Ihre eigene Pufferung Implementierung - Python string.find und string.split und so können erstaunlich schnell sein. Anrufe unnötig

def linesplit(socket): 
    buffer = socket.recv(4096) 
    buffering = True 
    while buffering: 
     if "\n" in buffer: 
      (line, buffer) = buffer.split("\n", 1) 
      yield line + "\n" 
     else: 
      more = socket.recv(4096) 
      if not more: 
       buffering = False 
      else: 
       buffer += more 
    if buffer: 
     yield buffer 

Wenn Sie die Nutzlast besteht aus Linien erwarten, die nicht zu groß sind, die ziemlich schnell laufen sollten, und vermeiden durch zu viele Schichten von Funktion springen. Ich wäre interessant zu wissen, wie dies mit file.readline() oder mit socket.recv (1) vergleicht.

6
def buffered_readlines(pull_next_chunk, buf_size=4096): 
    """ 
    pull_next_chunk is callable that should accept one positional argument max_len, 
    i.e. socket.recv or file().read and returns string of up to max_len long or 
    empty one when nothing left to read. 

    >>> for line in buffered_readlines(socket.recv, 16384): 
    ... print line 
    ... 
    >>> # the following code won't read whole file into memory 
    ... # before splitting it into lines like .readlines method 
    ... # of file does. Also it won't block until FIFO-file is closed 
    ... 
    >>> for line in buffered_readlines(open('huge_file').read): 
    ... # process it on per-line basis 
     ... 
    >>> 
    """ 
    chunks = [] 
    while True: 
    chunk = pull_next_chunk(buf_size) 
    if not chunk: 
     if chunks: 
     yield ''.join(chunks) 
     break 
    if not '\n' in chunk: 
     chunks.append(chunk) 
     continue 
    chunk = chunk.split('\n') 
    if chunks: 
     yield ''.join(chunks + [chunk[0]]) 
    else: 
     yield chunk[0] 
    for line in chunk[1:-1]: 
     yield line 
    if chunk[-1]: 
     chunks = [chunk[-1]] 
    else: 
     chunks = []