Соединение нескольких потоков в одном классе Stream - PullRequest
2 голосов
/ 19 октября 2010

Я хочу создать класс (давайте назовем класс HugeStream), который принимает IEnumerable<code><Stream> в своем конструкторе. Этот HugeStream должен реализовывать абстрактный класс Stream.

По сути, у меня есть от 1 до многих фрагментов потоков UTF8, поступающих из БД, которые при объединении создают гигантский документ XML. HugeStream должен быть заархивирован, чтобы я мог в любое время вернуться к позиции 0 всего сшитого вместе потока.

Кто-нибудь знает, как сделать это быстро?

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

Кстати, у меня возникли проблемы с визуализацией потоков, и теперь я немного сбит с толку, когда мне нужно реализовать собственный поток. Если есть хороший учебник по реализации класса Stream, о котором кто-нибудь знает, пожалуйста, дайте мне знать; Я не нашел хороших статей, просматривающих вокруг. Я просто вижу много статей об использовании уже существующих FileStreams и MemoryStreams. Я очень визуальный ученик и по какой-то причине не могу найти ничего полезного для изучения концепции.

Спасибо

Мэтт

1 Ответ

4 голосов
/ 19 октября 2010

Если вы только последовательно считываете данные из HugeStream, тогда ему просто нужно прочитать каждый дочерний поток (и добавить его в локальный файл, а также вернуть прочитанные данные вызывающей стороне), пока дочерний поток не будет исчерпан, тогда перейти к следующему дочернему потоку. Если операция поиска используется для перехода «назад» в данных, вы должны начать чтение из файла локального кэша; когда вы достигнете конца файла кэша, вы должны возобновить чтение текущего дочернего потока, с которого вы остановились.

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

Неэффективность цитируемой статьи заключается в том, что она проходит по всем потокам каждый раз, когда вы читаете, чтобы определить, откуда продолжить чтение. Чтобы улучшить это, вам нужно открывать дочерние потоки только по мере их необходимости и отслеживать текущий открытый поток, чтобы вы могли просто продолжать читать больше данных из этого текущего потока, пока он не будет исчерпан. Затем откройте следующий поток как ваш «текущий» поток и продолжайте. Это довольно просто, так как у вас есть линейная последовательность потоков, поэтому вы просто шагаете по ним один за другим. то есть что-то вроде:

int currentStreamIndex = 0;
Stream currentStream = childStreams[currentStreamIndex++];

...

public override int Read(byte[] buffer, int offset, int count)
{
    while (count > 0)
    {
        // Read what we can from the current stream
        int numBytesRead = currentSteam.Read(buffer, offset, count);
        count -= numBytesRead;
        offset += numBytesRead;

        // If we haven't satisfied the read request, we have exhausted the child stream.
        // Move on to the next stream and loop around to read more data.
        if (count > 0)
        {
            // If we run out of child streams to read from, we're at the end of the HugeStream, and there is no more data to read
            if (currentStreamIndex >= numberOfChildStreams)
                break;

            // Otherwise, close the current child-stream and open the next one
            currentStream.Close();
            currentStream = childStreams[currentStreamIndex++];
        }
    }

   // Here, you'd write the data you've just read (into buffer) to your local cache stream
}

Чтобы разрешить поиск в обратном направлении, вам просто нужно ввести новый локальный файловый поток, в который вы копируете все данные во время чтения (см. Комментарий в моем псевдокоде выше). Вам нужно ввести состояние, чтобы вы знали, что вы читаете из файла кеша, а не из текущего дочернего потока, а затем просто получаете прямой доступ к кешу (поиск и т. Д. Прост, потому что кеш представляет всю историю данных, прочитанных из HugeStream поэтому смещения поиска одинаковы для HugeStream и Cache - вам просто нужно перенаправить любые вызовы Read, чтобы вытащить данные из потока кэша)

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

Если вы хотите иметь возможность поддерживать полный произвольный доступ в HugeStream, вам нужно будет поддерживать поиск «вперед» (за пределами текущего конца потока кэша). Если вы заранее не знаете длины дочерних потоков, у вас нет другого выбора, кроме как просто читать данные в свой кэш, пока не достигнете смещения поиска. Если вы знаете размеры всех потоков, то вы можете искать прямо и более эффективно в нужном месте, но тогда вам придется разработать эффективный способ хранения данных, которые вы читаете, в файл кеша и записи, какие части кеша файл содержит действительные данные, которые еще не были прочитаны из БД - это немного сложнее.

Я надеюсь, что это имеет смысл для вас и дает вам лучшее представление о том, как действовать ...

(Вам не нужно реализовывать намного больше, чем интерфейсы Read и Seek, чтобы это работало).

...