Вопрос по stream, c#, .net, io – Как я могу разделить (скопировать) поток в .NET?

8

Кто-нибудь знает, где я могу найти реализацию разделителя потока?

Я хочу взять поток и получить два отдельных потока, которые можно независимо читать и закрывать, не влияя друг на друга. Каждый из этих потоков должен возвращать те же двоичные данные, что и исходный поток. Не нужно реализовывать Позицию или Поиск и все такое ... Только вперед

Я бы предпочел, чтобы он просто не копировал весь поток в память и не подавал его несколько раз, что было бы достаточно просто реализовать самому.

Есть ли что-нибудь, что могло бы сделать это?

Вероятно, он должен быть основан на кольцевом буфере. Я постараюсь написать быструю реализацию, если у меня будет время. Noldorin
Что-то вродеtee в UNIX ... Mehrdad Afshari

Ваш Ответ

7   ответов
3

не рискуя хранить все буферизованные в памяти (если потоки имеют значения BOF и EOF соответственно).

Интересно, не проще ли записать поток на диск, скопировать его и получить два потока, считывающих с диска, с самоудалением, встроенным вClose() (т.е. написать свойStream обертка вокругFileStream).

4

Вам нужно будет буферизовать данные из исходного потока способом FIFO, отбрасывая только те данные, которые были прочитаны всеми «читателями». потоки.

Я использую:

A "management" object holding some sort of queue of byte[] holding the chunks to be buffered and reading additional data from the source stream if required Some "reader" instances which known where and on what buffer they are reading, and which request the next chunk from the "management" and notify it when they don't use a chunk anymore, so that it may be removed from the queue
1

Я сделал SplitStream доступным на github и NuGet.

Это идет так.

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

Я не проверял это на очень больших потоках, но попробую.

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

2

Представленное ниже, вероятно, называется EchoStream http://www.codeproject.com/Articles/3922/EchoStream-An-Echo-Tee-Stream-for-NET Это очень старая реализация (2003), но она должна предоставлять некоторый контекст

найдено черезПеренаправление записи в файл в поток C #

1

Я не думаю, что вы сможете найти общую реализацию, чтобы сделать именно это. Поток является довольно абстрактным, вы не знаете, откуда поступают байты. Например, вы не знаете, будет ли это поддерживать поиск; и вы не знаете относительную стоимость операций. (Поток может быть абстракцией чтения данных с удаленного сервера или даже с ленты резервного копирования!).

Если вы можете использовать MemoryStream и сохранять содержимое один раз, вы можете создать два отдельных потока, используя один и тот же буфер; и они будут вести себя как независимые потоки, но использовать память только один раз.

В противном случае, я думаю, что вам лучше всего создать класс-оболочку, который хранит байты, считанные из одного потока, до тех пор, пока они не будут прочитаны вторым потоком. Это даст вам желаемое поведение только для пересылки, но в худшем случае вы можете рискнуть сохранить все байты в памяти, если второй поток не будет считан, пока первый поток не завершит чтение всего содержимого.

Какое применение это?
-1

С введением async / await, поскольку все ваши задачи чтения, кроме одной, являются асинхронными, вы сможете обрабатывать одни и те же данные дважды, используя только один поток ОС.

Я думаю, что вы хотите, это связанный список блоков данных, которые вы видели до сих пор. Затем вы можете иметь несколько пользовательских экземпляров Stream, которые содержат указатель на этот список. Когда блоки попадают в конец списка, они будут собирать мусор. Немедленное повторное использование памяти потребовало бы некоторого другого вида циклического списка и подсчета ссылок. Выполнимо, но сложнее.

Когда ваш пользовательский поток может ответить на вызов ReadAsync из кэша, скопируйте данные, переместите указатель вниз по списку и вернитесь.

Когда ваш поток достиг конца списка кэша, вы хотите выполнить один ReadAsync для базового потока, не ожидая его, и кэшировать возвращенную задачу с помощью блока данных. Поэтому, если какой-либо другой считыватель Stream также догоняет и пытается прочитать больше, прежде чем это чтение завершится, вы можете вернуть тот же объект Task.

Таким образом, оба читателя подключат свое ожидание продолжения к результату одного и того же вызова ReadAsync. Когда возвращается одиночное чтение, обе задачи чтения будут последовательно выполнять следующий шаг своего процесса.

2

Вы действительно не можете сделать это, не дублируя, по крайней мере, часть потока источника - в основном из-за того, что если звук не похож на ваш, вы можете контролировать скорость, с которой они потребляются (несколько потоков?). Вы могли бы сделать что-то умное в отношении одного чтения, а не другого (и, следовательно, сделать копию только в этот момент), но сложность этого звучит так, будто это не стоит проблем.

не говоря уже о том, что если он используется в многопоточном сценарии, вы не позволяете ОС / платформе использовать собственные встроенные механизмы для нескольких считывателей одного и того же файла. При использовании в памяти наихудшим случаем всегда будет то, что вам, возможно, придется скопировать весь поток в любом случае, поэтому попытка чего-то подобного, возможно, потребует немало усилий, чтобы заметить ... возможно, будет лучше работать модель с несколькими потребителями.

Похожие вопросы