Puoi spiegare il concetto di stream?


186

Capisco che uno stream è una rappresentazione di una sequenza di byte. Ciascun flusso fornisce mezzi per leggere e scrivere byte nel relativo archivio di backup. Ma qual è il punto del flusso? Perché il negozio di supporto non è quello con cui interagiamo?

Per qualsiasi motivo questo concetto non fa clic per me. Ho letto un sacco di articoli, ma penso di aver bisogno di un'analogia o qualcosa del genere.

Risposte:


234

La parola "flusso" è stata scelta perché rappresenta (nella vita reale) un significato molto simile a ciò che vogliamo trasmettere quando lo usiamo.

Dimentichiamoci un po 'del negozio di supporto e iniziamo a pensare all'analogia con un flusso d'acqua. Ricevi un flusso continuo di dati, proprio come l'acqua scorre continuamente in un fiume. Non necessariamente sai da dove provengono i dati e molto spesso non è necessario; che si tratti di un file, di un socket o di qualsiasi altra fonte, non ha (non dovrebbe) importare davvero. Questo è molto simile alla ricezione di un flusso d'acqua, per cui non è necessario sapere da dove provenga; che si tratti di un lago, una fontana o qualsiasi altra fonte, non ha (non dovrebbe) importare davvero.

Detto questo, una volta che inizi a pensare che ti interessa solo ottenere i dati di cui hai bisogno, indipendentemente da dove provengano, le astrazioni di cui altre persone hanno parlato diventano più chiare. Inizi a pensare di poter avvolgere i flussi e i tuoi metodi continueranno a funzionare perfettamente. Ad esempio, potresti farlo:

int ReadInt(StreamReader reader) { return Int32.Parse(reader.ReadLine()); }

// in another method:
Stream fileStream = new FileStream("My Data.dat");
Stream zipStream = new ZipDecompressorStream(fileStream);
Stream decryptedStream = new DecryptionStream(zipStream);
StreamReader reader = new StreamReader(decryptedStream);

int x = ReadInt(reader);

Come vedi, diventa molto facile cambiare la tua sorgente di input senza cambiare la tua logica di elaborazione. Ad esempio, per leggere i dati da un socket di rete anziché da un file:

Stream stream = new NetworkStream(mySocket);
StreamReader reader = new StreamReader(stream);
int x = ReadInt(reader);

Facile come può essere. E la bellezza continua, poiché puoi utilizzare qualsiasi tipo di sorgente di input, purché tu possa creare un "wrapper" di flusso per esso. Potresti persino farlo:

public class RandomNumbersStreamReader : StreamReader {
    private Random random = new Random();

    public String ReadLine() { return random.Next().ToString(); }
}

// and to call it:
int x = ReadInt(new RandomNumbersStreamReader());

Vedere? Fintanto che al tuo metodo non importa quale sia la fonte di input, puoi personalizzare la tua fonte in vari modi. L'astrazione consente di separare l'input dalla logica di elaborazione in un modo molto elegante.

Si noti che il flusso che abbiamo creato noi stessi non ha un archivio di supporto, ma serve comunque perfettamente ai nostri scopi.

Quindi, per riassumere, uno stream è solo una fonte di input, nascondendo (astrattando) un'altra fonte. Finché non rompi l'astrazione, il tuo codice sarà molto flessibile.


6
Il pensiero astratto (e la spiegazione) sembrano essere nel tuo sangue;) La tua analogia con l'acqua (e quindi i riferimenti metaforici) mi ha ricordato Omar Khayyam.
java.is.for.desktop,

@HosamAly La tua spiegazione è molto chiara ma qualcosa mi confonde un po 'nel codice di esempio. La conversione esplicita da stringa a int viene eseguita automaticamente facendo ReadInt? credo di poter fare anche ReadString?
Rushino,

1
@Rushino Non ci sono conversioni nel codice sopra. Il metodo ReadIntviene definito in alto usando int.Parse, che riceve la stringa restituita reader.ReadLine()e la analizza. Ovviamente potresti creare un ReadStringmetodo simile . È abbastanza chiaro?
Hosam Aly,

Ben messo. I flussi per me sono le astrazioni generiche più semplici e potenti nell'intera programmazione. Avere .net basic Stream.Copysemplifica la vita in molte applicazioni.
Felype,

38

Il punto è che non dovresti sapere qual è il negozio di supporto - è un'astrazione su di esso. In effetti, potrebbe non esserci nemmeno un archivio di backup: potresti leggere da una rete e i dati non vengono mai "archiviati".

Se riesci a scrivere codice che funziona sia che tu stia parlando con un file system, memoria, una rete o qualsiasi altra cosa che supporti l'idea dello stream, il tuo codice è molto più flessibile.

Inoltre, i flussi sono spesso concatenati: puoi avere un flusso che comprime tutto ciò che viene inserito, scrivendo il modulo compresso su un altro flusso o uno che crittografa i dati, ecc. All'altra estremità ci sarebbe il contrario catena, decrittografia, decompressione o altro.


I diversi tipi di lettori di stream utilizzati nell'esempio @HosamAly sopra non implicano che tu sappia qual è il backing store? Lo prendo FileStream, NetworkStream ecc ... stanno leggendo da quei tipi di fonti. Inoltre, ci sono casi in cui non sai quale potrebbe essere l'archivio di backup e che verrebbero scelti dinamicamente durante l'esecuzione del programma? Non l'ho mai trovato personalmente e vorrei saperne di più.
user137717

Inoltre, i flussi possono trasmettere i dati attraverso un certo processo man mano che i dati vengono generati o devo accedere al set di dati completo su cui voglio operare quando inizio il processo?
user137717

@ user137717: No, se prendi semplicemente un StreamReader- o meglio, un TextReaderallora il tuo codice non sa quale tipo di flusso sia alla base del flusso di dati. O meglio, può usare la BaseStreamproprietà per scoprire il tipo, ma potrebbe essere un tipo che il tuo codice non ha mai visto prima. Il punto è che non ti dovrebbe interessare. E sì, puoi assolutamente finire per scrivere codice che a volte verrà utilizzato per un flusso di rete e talvolta per un flusso di file. Per quanto riguarda i flussi di dati di piping attraverso un processo - beh, ciò non verrebbe fatto all'interno del processo ... sarebbe il provider del flusso.
Jon Skeet,

30

Il punto del flusso è fornire uno strato di astrazione tra te e il negozio di supporto. Pertanto, un determinato blocco di codice che utilizza un flusso non deve preoccuparsi se l'archivio di backup è un file su disco, memoria, ecc ...


Sì, ti consente di scambiare il tipo di flusso senza rompere il codice. Ad esempio, è possibile leggere da un file su una chiamata e quindi un buffer di memoria sul successivo.
Craig,

Aggiungo che il motivo per cui vorresti farlo è che spesso non hai bisogno della capacità di ricerca di file durante la lettura o la scrittura di un file, e quindi se usi un flusso lo stesso codice può essere facilmente utilizzato per leggere o scrivere su un socket di rete, ad esempio.
alxp,

11

Non si tratta di corsi d'acqua - si tratta di nuotare. Se puoi nuotare in uno Stream, allora puoi nuotare in qualsiasi Stream che incontri.


7

Per aggiungere alla camera di eco, il flusso è un'astrazione, quindi non ti interessa il negozio sottostante. Ha più senso se si considerano scenari con e senza flussi.

I file non sono per la maggior parte interessanti perché gli stream non fanno molto al di sopra e al di là di quali metodi non basati su stream conosco. Cominciamo con i file di Internet.

Se voglio scaricare un file da Internet, devo aprire un socket TCP, stabilire una connessione e ricevere byte fino a quando non ci sono più byte. Devo gestire un buffer, conoscere la dimensione del file previsto e scrivere il codice per rilevare quando la connessione viene interrotta e gestirla in modo appropriato.

Diciamo che ho una sorta di oggetto TcpDataStream. Lo creo con le informazioni di connessione appropriate, quindi leggo i byte dallo stream fino a quando non dice che non ci sono più byte. Il flusso gestisce la gestione del buffer, le condizioni di fine dei dati e la gestione della connessione.

In questo modo, i flussi semplificano l'I / O. Potresti certamente scrivere una classe TcpFileDownloader che fa ciò che fa lo stream, ma poi hai una classe specifica per TCP. La maggior parte delle interfacce di flusso fornisce semplicemente un metodo Read () e Write () e qualsiasi altro concetto più complicato viene gestito dall'implementazione interna. Per questo motivo, è possibile utilizzare lo stesso codice di base per leggere o scrivere in memoria, file su disco, socket e molti altri archivi di dati.


5

La visualizzazione che uso sono nastri trasportatori, non in fabbriche reali perché non ne so nulla, ma in fabbriche di cartoni animati in cui gli oggetti si muovono lungo le linee e vengono stampati, inscatolati e contati e controllati da una sequenza di dispositivi stupidi.

Hai componenti semplici che fanno una cosa, ad esempio un dispositivo per mettere una ciliegia su una torta. Questo dispositivo ha un flusso di input di torte senza ciliegia e un flusso di output di torte con ciliegie. Ci sono tre vantaggi che vale la pena menzionare strutturare la tua elaborazione in questo modo.

Innanzitutto semplifica i componenti stessi: se vuoi mettere una glassa al cioccolato su una torta, non hai bisogno di un dispositivo complicato che sappia tutto sulle torte, puoi creare un dispositivo stupido che attacca la glassa al cioccolato su tutto ciò che viene inserito (in i cartoni animati, questo arriva fino a non sapere che l'articolo successivo non è una torta, è Wile E. Coyote).

In secondo luogo puoi creare prodotti diversi inserendo i dispositivi in ​​sequenze diverse: forse vuoi che le tue torte abbiano la glassa sopra la ciliegia invece della ciliegia sopra la glassa, e puoi farlo semplicemente scambiando i dispositivi sulla linea .

In terzo luogo, i dispositivi non devono gestire inventario, boxe o unboxing. Il modo più efficiente di aggregare e confezionare le cose è mutevole: forse oggi stai mettendo le tue torte in scatole da 48 e le invii con il camion, ma domani vuoi inviare scatole da sei in risposta a ordini personalizzati. Questo tipo di cambiamento può essere adattato sostituendo o riconfigurando le macchine all'inizio e alla fine della linea di produzione; la macchina della ciliegia nel mezzo della linea non deve essere cambiata per elaborare un numero diverso di elementi alla volta, funziona sempre con un elemento alla volta e non deve sapere come sia l'input o l'output essere raggruppati.


Grande esempio di analogia come spiegazione.
Richie Thomas,

5

Quando ho sentito parlare dello streaming per la prima volta, era nel contesto dello streaming live con una webcam. Pertanto, un host sta trasmettendo contenuti video e l'altro host sta ricevendo i contenuti video. Quindi questo streaming? Bene ... sì ... ma un live stream è un concetto concreto e penso che la domanda si riferisca al concetto astratto di Streaming. Vedi https://en.wikipedia.org/wiki/Live_streaming

Quindi andiamo avanti.


Il video non è l'unica risorsa che può essere trasmessa in streaming. Anche l'audio può essere riprodotto in streaming. Quindi stiamo parlando di streaming media ora. Vedi https://en.wikipedia.org/wiki/Streaming_media . L'audio può essere trasmesso dalla sorgente alla destinazione in numerosi modi. Quindi confrontiamo alcuni metodi di consegna dei dati tra loro.

Download di file classico Il download di file classico non avviene in tempo reale. Prima di utilizzare il file, dovrai attendere fino al completamento del download.

Download progressivo I blocchi di download progressivo scaricano i dati dal file multimediale in streaming in un buffer temporaneo. I dati in quel buffer sono fattibili: i dati audio-video nel buffer sono riproducibili. Per questo motivo gli utenti possono guardare / ascoltare il file multimediale in streaming durante il download. L'avanzamento rapido e il riavvolgimento sono possibili, fuori dal campo con il buffer. Ad ogni modo, il download progressivo non è streaming live.

Streaming Succede in tempo reale e blocchi di dati. Lo streaming è implementato nelle trasmissioni in diretta. I clienti che ascoltano la trasmissione non possono avanzare o riavvolgere rapidamente. Negli stream video, i dati vengono eliminati dopo la riproduzione.

Un server di streaming mantiene una connessione a 2 vie con il suo client, mentre un server Web chiude la connessione dopo una risposta del server.


Audio e video non sono l'unica cosa che può essere trasmessa in streaming. Diamo un'occhiata al concetto di stream nel manuale di PHP.

uno stream è un oggetto risorsa che presenta comportamenti scorrevoli. Cioè, può essere letto o scritto in modo lineare e può essere in grado di cercare () in una posizione arbitraria all'interno del flusso. Link: https://www.php.net/manual/en/intro.stream.php

In PHP, una risorsa è un riferimento a una fonte esterna come un file, una connessione al database. Quindi, in altre parole, uno stream è una fonte che può essere letta o scritta. Quindi, se hai lavorato con fopen(), hai già lavorato con i flussi.

Un esempio di un file di testo soggetto allo streaming:

// Let's say that cheese.txt is a file that contains this content: 
// I like cheese, a lot! My favorite cheese brand is Leerdammer.
$fp = fopen('cheese.txt', 'r');

$str8 = fread($fp, 8); // read first 8 characters from stream. 

fseek($fp, 21); // set position indicator from stream at the 21th position (0 = first position)
$str30 = fread($fp, 30); // read 30 characters from stream

echo $str8; // Output: I like c 
echo $str30; // Output: My favorite cheese brand is L

Anche i file zip possono essere trasmessi in streaming. Inoltre, lo streaming non è limitato ai file. È inoltre possibile eseguire lo streaming di connessioni HTTP, FTP, SSH e Input / Output.


Cosa dice Wikipedia sul concetto di streaming?

Nell'informatica, uno stream è una sequenza di elementi di dati resi disponibili nel tempo. Un flusso può essere pensato come elementi su un nastro trasportatore che vengono elaborati uno alla volta anziché in grandi lotti.

Vedi: https://en.wikipedia.org/wiki/Stream_%28computing%29 .

Wikipedia si collega a questo: https://srfi.schemers.org/srfi-41/srfi-41.html e gli autori hanno questo da dire sugli stream:

Gli stream, a volte chiamati elenchi pigri, sono una struttura di dati sequenziale contenente elementi calcolati solo su richiesta. Uno stream è nullo o è una coppia con uno stream nel suo cdr. Poiché gli elementi di un flusso vengono calcolati solo quando si accede, i flussi possono essere infiniti.

Quindi uno Stream è in realtà una struttura di dati.


La mia conclusione: uno stream è una fonte che può contenere dati che possono essere letti o scritti in modo sequenziale. Un flusso non legge tutto ciò che contiene la fonte in una sola volta, legge / scrive in sequenza.


Link utili:

  1. http://www.slideshare.net/auroraeosrose/writing-and-using-php-streams-and-sockets-zendcon-2011 Fornisce una presentazione molto chiara
  2. https://www.sk89q.com/2010/04/introduction-to-php-streams/
  3. http://www.netlingo.com/word/stream-or-streaming.php
  4. http://www.brainbell.com/tutorials/php/Using_PHP_Streams.htm
  5. http://www.sitepoint.com/php-streaming-output-buffering-explained/
  6. http://php.net/manual/en/wrappers.php
  7. http://www.digidata-lb.com/streaming/Streaming_Proposal.pdf
  8. http://www.webopedia.com/TERM/S/streaming.html
  9. https://en.wikipedia.org/wiki/Stream_%28computing%29
  10. https://srfi.schemers.org/srfi-41/srfi-41.html

4

È solo un concetto, un altro livello di astrazione che ti semplifica la vita. E tutti hanno un'interfaccia comune, il che significa che puoi combinarli in un modo simile a una pipe. Ad esempio, codifica su base64, quindi zip e quindi scrivi questo su disco e tutto in una riga!


Questo è utile, certamente, ma non direi che è "il punto". Anche senza concatenare è utile avere un'astrazione comune.
Jon Skeet,

Si hai ragione. Ho cambiato le parole per chiarirlo.
vava,

Sì, va meglio. Spero non pensassi che fossi troppo esigente!
Jon Skeet,

3

La migliore spiegazione dei flussi che ho visto è il capitolo 3 di SICP . (Potrebbe essere necessario leggere i primi 2 capitoli per avere un senso, ma dovresti comunque. :-)

Non usano affatto gli steram per i byte, ma piuttosto i numeri interi. I grandi punti che ne ho tratto sono stati:

  • Gli stream sono elenchi ritardati
  • Il sovraccarico computazionale [di calcolare avidamente tutto in anticipo, in alcuni casi] è scandaloso
  • Possiamo usare gli stream per rappresentare sequenze infinitamente lunghe

Attualmente sono al capitolo 1 del SICP. Grazie!
Rob Sobers,

2
si vorrebbe dire agli altri lo stream SICP . una caratteristica importante del flusso SICP è la pigrizia , mentre il concetto di flusso generico enfatizza l' astrazione sulle sequenze di dati .
象 嘉 道

2

Un altro punto (per leggere la situazione del file):

  1. streampuò permetterti di fare qualcos'altro prima finished reading all content of the file.
  2. puoi risparmiare memoria, perché non è necessario caricare tutto il contenuto del file in una sola volta.

1

Pensa ai flussi come a una fonte astratta di dati (byte, caratteri, ecc.). Estraggono i meccanismi reali di lettura e scrittura sull'origine dati concreta, che si tratti di un socket di rete, di un file su un disco o di una risposta dal server web.


1

Penso che devi considerare che il negozio di supporto stesso è spesso solo un'altra astrazione. Un flusso di memoria è piuttosto semplice da capire, ma un file è radicalmente diverso a seconda del file system in uso, indipendentemente dal disco rigido che si sta utilizzando. In realtà non tutti gli stream si trovano in cima a un archivio di supporto: gli stream di rete praticamente sono solo stream.

Il punto di un flusso è che limitiamo la nostra attenzione a ciò che è importante. Avendo un'astrazione standard, possiamo eseguire operazioni comuni. Anche se non vuoi, ad esempio, cercare un file o una risposta HTTP per gli URL oggi, non significa che non vorrai farlo domani.

Gli stream erano originariamente concepiti quando la memoria era minuscola rispetto alla memoria. La sola lettura di un file C potrebbe essere un carico significativo. Ridurre al minimo l'ingombro della memoria era estremamente importante. Quindi, un'astrazione in cui era necessario caricare pochissimo era molto utile. Oggi è ugualmente utile quando si esegue la comunicazione di rete e, si scopre, raramente così restrittivo quando si tratta di file. La possibilità di aggiungere in modo trasparente cose come il buffering in modo generale lo rende ancora più utile.


0

Un flusso è un'astrazione di una sequenza di byte. L'idea è che non è necessario sapere da dove provengono i byte, solo che è possibile leggerli in modo standardizzato.

Ad esempio, se si elaborano dati tramite un flusso, non importa al proprio codice se i dati provengono da un file, una connessione di rete, una stringa, un BLOB in un database ecc. Ecc. Ecc.

Non c'è nulla di sbagliato di per sé nell'interazione con il backing store stesso, tranne per il fatto che ti lega all'implementazione del backing store.


0

Un flusso è un'astrazione che fornisce un insieme standard di metodi e proprietà per l'interazione con i dati. Estrarre dall'attuale supporto di memorizzazione, il codice può essere scritto senza fare totale affidamento su ciò che è quel supporto o addirittura sull'implementazione di quel supporto.

Una buona analogia potrebbe essere quella di considerare una borsa. Non ti importa di cosa sia fatta una borsa o di cosa faccia quando ci metti le tue cose, purché la borsa svolga il compito di essere una borsa e tu riesca a riprenderla. Un flusso definisce per i supporti di memorizzazione ciò che il concetto di borsa definisce per le diverse istanze di una borsa (come sacco della spazzatura, borsa, zaino, ecc.) - le regole di interazione.


0

Lo terrò breve, mi mancava solo la parola qui:

Gli stream sono code normalmente archiviate nel buffer contenente qualsiasi tipo di dati.

(Ora, poiché sappiamo tutti quali sono le code, non è necessario spiegarlo ulteriormente.)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.