2009-06-28 6 views
6

Czy ktoś wie, gdzie mogę znaleźć implementację rozdzielacza strumienia?Jak mogę podzielić (skopiować) strumień w .NET?

Szukam wziąć strumień i uzyskać dwa oddzielne strumienie, które mogą być niezależnie odczytywane i zamykane bez wpływu na siebie. Strumienie te powinny zwracać te same dane binarne, co oryginalny strumień. Nie ma potrzeby wdrażania pozycji lub wyszukiwania i takich ... Przekazywanie tylko.

Wolałbym, żeby nie skopiował całego strumienia do pamięci i nie serwował go wiele razy, co byłoby dość proste do wdrożenia.

Czy jest coś, co mogłoby to zrobić?

+1

Coś 'tee' w UNIX ... –

+0

Prawdopodobnie muszą opierać się wokół okrągłego bufora. Spróbuję napisać szybką implementację, jeśli dostanę czas. – Noldorin

Odpowiedz

4

Nie po wyjęciu z pudełka.

Będziesz musiał buforować dane z oryginalnego strumienia w sposób FIFO, odrzucając tylko dane, które zostały przeczytane przez wszystkie strumienie "czytnika".

użyję:

„zarządzanie” obiekt
  • gospodarstwie jakieś kolejki byte [] trzymając kawałki mają być buforowane i czytanie dodatkowych danych ze strumienia źródłowego jeśli wymagane
  • jakiś " "instancje", które wiedziały, gdzie i na jakim buforze czytają, i które żądają następnego fragmentu od "zarządzania" i powiadamiają go, gdy nie używają już porcji, tak aby można było usunąć z kolejki
1

Nie sądzę, że będziesz w stanie znaleźć ogólna implementacja, aby to zrobić. Strumień jest raczej abstrakcyjny, nie wiesz skąd pochodzą bajty. Na przykład nie wiesz, czy to pomoże w szukaniu; i nie znasz względnego kosztu operacji. (Strumień może być abstrakcją odczytu danych ze zdalnego serwera lub nawet z taśmy kopii zapasowej!).

Jeśli możesz mieć MemoryStream i zapisać zawartość raz, możesz utworzyć dwa oddzielne strumienie przy użyciu tego samego bufora; i będą zachowywać się jak niezależne strumienie, ale tylko raz wykorzystają pamięć.

W przeciwnym razie myślę, że najlepiej, tworząc klasę opakowania, która przechowuje bajty odczytane z jednego strumienia, dopóki nie zostaną odczytywane przez drugi strumień. Daje to pożądane zachowanie tylko do przodu, ale w najgorszym wypadku możesz ryzykować przechowywanie wszystkich bajtów w pamięci, jeśli drugi strumień nie zostanie odczytany, dopóki pierwszy strumień nie zakończy odczytu całej zawartości.

+0

Co to jest zastosowanie? – headsling

1

Naprawdę nie możesz tego zrobić bez duplikowania przynajmniej części strumienia z kumienia - głównie z powodu tego, że jeśli nie brzmisz, możesz kontrolować tempo, z jakim są konsumowane (wiele wątków?). Możesz zrobić coś sprytnego, jeśli chodzi o jedno czytanie drugiego z nich (i tym samym kopiowanie tylko w tym momencie), ale kompleks brzmi, jakby to nie było warte problemów.

+0

nie wspominając o tym, że jeśli jest używany w scenariuszu wielowątkowym, zatrzymujesz system operacyjny/platformę od korzystania z własnych mechanizmów wewnętrznych dla posiadania wielu czytników tego samego pliku. W przypadku użycia w pamięci najgorszym przypadkiem będzie zawsze to, że być może będziesz musiał skopiować cały strumień, tak więc próbując czegoś takiego, być może trzeba dużo wysiłku, aby zauważyć ... że popchnięcie do modelu wielu klientów może lepiej działać – ShuggyCoUk

3

Może to być trudne, nie ryzykując przechowywania wszystkich danych w pamięci (jeśli strumienie mają odpowiednio wartość BOF i EOF).

Zastanawiam się, czy nie jest łatwiej napisać strumień na dysk, skopiować go i mieć dwa strumienie odczyt z dysku, z samodzielnego usunięcia wbudowanego w Close() (tj napisać własny Stream owijkę wokół FileStream).

0

Wraz z wprowadzeniem async/Oczekujcie, tak długo, jak wszystkie oprócz jednego z twoich zadań czytania są asynchroniczne, powinieneś być w stanie przetworzyć te same dane dwa razy używając tylko jednego wątku systemu operacyjnego.

Co myślę, że chcesz, to połączona lista bloków danych, które widziałeś do tej pory. Następnie możesz mieć wiele niestandardowych instancji Stream, które trzymają wskaźnik na tej liście. Gdy bloki wypadną z końca listy, będą zbierane śmieci. Ponowne użycie pamięci wymagałoby innego rodzaju okrągłej listy i liczenia odwołań. Naprawalne, ale bardziej skomplikowane.

Kiedy niestandardowy strumień może odpowiedzieć na wywołanie ReadAsync z pamięci podręcznej, skopiuj dane, przesuń wskaźnik w dół listy i wróć.

Kiedy Twój strumień zostanie przechwycony na końcu listy pamięci podręcznej, chcesz wydać pojedynczą ReadAsync do strumienia bazowego, nie czekając na niego, i buforować zwrócone zadanie z blokiem danych. Więc jeśli inny czytnik strumienia również się zapisze i spróbuje przeczytać więcej zanim ten odczyt się zakończy, możesz zwrócić ten sam obiekt zadania.

W ten sposób obaj czytelnicy oczekują kontynuacji na wynik tego samego wywołania ReadAsync. Kiedy pojedynczy odczyt zostanie zwrócony, oba zadania czytania będą sekwencyjnie wykonywać następny krok w ich procesie.

0

Udostępniłem SplitStream na github i NuGet.

To działa tak.

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); 
     }, 
    ); 
} 

Nie testowałem tego na bardzo dużych strumieniach, ale spróbuj.

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

+0

Odpowiedzi bez rzeczywisty kod nie jest użyteczny lub pożądany w przypadku przepełnienia stosu. Dobra odpowiedź WR będzie całkowicie niezależna, w przeciwieństwie do całkowitego polegania na jakimś zewnętrznym zasobie, jak ma to miejsce w tym przypadku. Edytuj odpowiedź, aby czytelnicy mogli uzyskać wszystkie ważne informacje z samego answer post. –