2009-06-28 8 views
6

Weiß jemand, wo ich eine Stream-Splitter-Implementierung finden kann?Wie kann ich einen Stream in .NET teilen (kopieren)?

Ich bin auf der Suche nach einem Stream, und erhalten zwei separate Streams, die unabhängig voneinander gelesen und geschlossen werden können, ohne sich gegenseitig zu beeinflussen. Diese Streams sollten jeweils die gleichen Binärdaten zurückgeben wie der ursprüngliche Stream. Keine Notwendigkeit, Position oder Suche zu implementieren und so weiter ... Nur vorwärts.

Ich würde es vorziehen, wenn es nicht nur den gesamten Strom in den Speicher kopieren und es mehrmals bereitstellen würde, was ziemlich einfach genug wäre, um mich selbst zu implementieren.

Gibt es da draußen etwas, das das tun könnte?

+1

So etwas wie 'tee' in UNIX ... –

+0

Es wahrscheinlich um einen Ringpuffer basiert werden müssen. Ich werde versuchen, eine schnelle Implementierung zu schreiben, wenn ich Zeit bekomme. – Noldorin

Antwort

4

Nicht im Lieferumfang enthalten.

Sie müssen die Daten aus dem ursprünglichen Datenstrom in einer FIFO-Weise puffern und nur Daten löschen, die von allen "Leser" -Streams gelesen wurden.

Ich würde verwenden:

  • A „Management“ Objekt eine Art Warteschlange von byte [] hält die Stücke halten zusätzliche Daten aus dem Quellstrom wird gepuffert und zu lesen, wenn
  • Some "erforderlich Leser "Instanzen, die wissen, wo und auf welchen Puffer sie lesen, und welche den nächsten Chunk von der" Verwaltung "anfordern und sie benachrichtigen, wenn sie keinen Chunk mehr verwenden, so dass sie aus der Queue entfernt werden können
1

Ich glaube nicht, dass Sie in der Lage sein werden, ein zu finden generische Implementierung, um genau das zu tun. Ein Stream ist eher abstrakt, Sie wissen nicht, woher die Bytes kommen. Zum Beispiel wissen Sie nicht, ob es das Suchen unterstützt; und Sie kennen die relativen Betriebskosten nicht. (Der Stream könnte eine Abstraktion beim Lesen von Daten von einem entfernten Server oder sogar von einem Sicherungsband sein!).

Wenn Sie einen MemoryStream haben und den Inhalt einmal speichern können, können Sie zwei separate Datenströme mit dem gleichen Puffer erstellen; und sie werden sich als unabhängige Streams verhalten, aber den Speicher nur einmal benutzen.

Ansonsten denke ich, dass Sie am besten sind, indem Sie eine Wrapper-Klasse erstellen, die die gelesenen Bytes aus einem Stream speichert, bis sie auch vom zweiten Stream gelesen werden. Das würde Ihnen das gewünschte Nur-Vorwärts-Verhalten geben - aber im schlimmsten Fall könnten Sie riskieren, alle Bytes im Speicher zu speichern, wenn der zweite Stream nicht gelesen wird, bis der erste Stream den gesamten Inhalt gelesen hat.

+0

Was ist die Anwendung davon? – headsling

1

Sie können dies nicht tun, ohne zumindest einen Teil des Sourse-Streams zu duplizieren - hauptsächlich aufgrund der Tatsache, dass, wenn es nicht so klingt, Sie die Rate kontrollieren können, mit der sie verbraucht werden (mehrere Threads?). Man könnte etwas schlaues tun, wenn man das eine liest und das andere liest (und damit die Kopie nur an diesem Punkt macht), aber die Komplexität klingt so, als wäre es die Mühe nicht wert.

+0

nicht zu erwähnen, dass, wenn es in einem Multithread-Szenario verwendet wird, Sie die OS/Plattform stoppen, eigene intrinsische Mechanismen für mehrere Leser der gleichen Datei zu verwenden. Wenn im Speicher der schlimmste Fall immer verwendet wird, dass Sie möglicherweise den gesamten Stream sowieso kopieren müssen, so etwas zu versuchen ist möglicherweise eine Menge Mühe zu beachten ... würde ein Push zu mehreren Verbrauchern Modell besser funktionieren vielleicht – ShuggyCoUk

3

Dies könnte schwierig sein, ohne zu riskieren, alles im Speicher gepuffert zu halten (wenn die Ströme BOF und EOF sind).

Ich frage mich, ob es nicht einfacher ist, den Strom auf die Festplatte zu schreiben, kopieren Sie sie und haben zwei von der Platte zu lesen Strömen, mit Selbst Deletion in das Close() gebaut (das heißt Ihre eigene Stream Wrapper schreiben um FileStream).

0

Mit der Einführung von Asynchron/await, so lange einige Kontext

gefunden bieten als Alle bis auf eine Ihrer Leseaufgaben sind asynchron. Sie sollten dieselben Daten zweimal mit nur einem einzelnen Betriebssystem-Thread verarbeiten können.

Was ich denke, Sie wollen, ist eine verknüpfte Liste der Datenblöcke, die Sie bisher gesehen haben. Dann können Sie mehrere benutzerdefinierte Stream-Instanzen haben, die einen Zeiger in diese Liste enthalten. Wenn Blöcke vom Ende der Liste fallen, werden sie als Müll gesammelt. Die sofortige Wiederverwendung des Speichers würde eine andere Art von Umlauflisten- und Referenzzählung erfordern. Machbar, aber komplizierter.

Wenn Ihr angepasster Stream einen ReadAsync-Aufruf aus dem Cache beantworten kann, kopieren Sie die Daten, verschieben Sie den Zeiger in der Liste nach unten und kehren Sie zurück.

Wenn Ihr Stream das Ende der Cache-Liste erreicht hat, möchten Sie ein einzelnes ReadAsync für den zugrunde liegenden Stream ausgeben, ohne darauf zu warten und die zurückgegebene Aufgabe mit dem Datenblock zwischenzuspeichern. Wenn also ein anderer Stream-Reader ebenfalls aufholt und versucht, mehr zu lesen, bevor dieser Lesevorgang abgeschlossen ist, können Sie dasselbe Task-Objekt zurückgeben.

Auf diese Weise haken beide Leser ihre Wartefortsetzung auf das Ergebnis des gleichen ReadAsync-Aufrufs. Wenn der einzelne Lesevorgang zurückkehrt, führen beide Lesevorgänge den nächsten Schritt ihres Prozesses sequenziell aus.

0

Ich habe einen SplitStream auf GitHub und NuGet verfügbar gemacht.

Es geht so.

using (var inputSplitStream = new ReadableSplitStream(inputSourceStream)) 

using (var inputFileStream = inputSplitStream.GetForwardReadOnlyStream()) 
using (var outputFileStream = File.OpenWrite("MyFileOnAnyFilestore.bin")) 

using (var inputSha1Stream = inputSplitStream.GetForwardReadOnlyStream()) 
using (var outputSha1Stream = SHA1.Create()) 
{ 
    inputSplitStream.StartReadAhead(); 

    Parallel.Invoke(
     () => { 
      var bytes = outputSha1Stream.ComputeHash(inputSha1Stream); 
      var checksumSha1 = string.Join("", bytes.Select(x => x.ToString("x"))); 
     }, 
     () => { 
      inputFileStream.CopyTo(outputFileStream); 
     }, 
    ); 
} 

Ich habe es nicht auf sehr großen Streams getestet, aber versuchen Sie es.

Github: https://github.com/microknights/SplitStream

+0

Antworten ohne tatsächlicher Code ist bei Stack Overflow nicht sinnvoll oder wünschenswert. Eine gute SO-Antwort wird völlig in sich abgeschlossen sein, im Gegensatz zur vollständigen Abhängigkeit von einer externen Ressource, wie es hier der Fall ist. Bitte bearbeiten Sie Ihre Antwort, damit die Leser alle wichtigen Informationen aus dem Antwortpost selbst erhalten können. –