Зачем писать в Stream кусками?

Мне интересно, почему так много примеров читают массивы байтов в потоки в патронах, а не все сразу... Я знаю, что это сложный вопрос, но мне интересно.

Я немного разбираюсь в оборудовании, и буферы заполнения могут сильно зависеть от размера, и вы не захотите снова писать в буфер, пока он не будет сброшен туда, куда он должен идти и т. д. ... но с платформой .Net (и другими современные языки) я вижу примеры обоих. Итак, когда использовать какой и когда, или второй абсолютный нет нет?

Вот что я имею в виду (код):

var buffer = new byte[4096];

while (true)
{
    var read = this.InputStream.Read(buffer, 0, buffer.Length);

    if (read == 0)
        break;

    OutputStream.Write(buffer, 0, read);
}

скорее, чем:

var buffer = new byte[InputStream.Length];

var read = this.InputStream.Read(buffer, 0, buffer.Length);

OutputStream.Write(buffer, 0, read);

Я считаю, что оба законны? Так зачем проходить через всю возню с циклом while (в чем бы вы ни решили его структурировать)?

Я играю здесь в адвоката дьявола, так как хочу узнать как можно больше :)


person tigerswithguitars    schedule 28.11.2012    source источник


Ответы (4)


В первом случае все, что вам нужно, это 4 КБ памяти. Во втором случае вам нужно столько памяти, сколько занимают данные входного потока. Если входной поток 4 ГБ, вам нужно 4 ГБ.

Как вы думаете, было бы хорошо, если бы для операции копирования файлов требовалось 4 ГБ ОЗУ? Что, если бы вы подготовили образ диска размером 20 ГБ?

Есть еще такая штука с трубами. Вы не часто используете их в Windows, но подобный случай часто наблюдается в других операционных системах. Второй случай ждет, пока все данные будут прочитаны, и только потом записывает их на выход. Однако иногда рекомендуется записывать данные как можно быстрее — в первом случае запись в выходной поток начнется, как только будут прочитаны первые 4 КБ ввода. Подумайте об обслуживании веб-страниц: желательно, чтобы веб-сервер отправлял данные как можно скорее, чтобы веб-браузер клиента начал отображать заголовки и первую часть контента, не дожидаясь всего тела.

Однако, если вы знаете, что входной поток не будет больше 4 КБ, то оба случая эквивалентны.

person liori    schedule 28.11.2012
comment
В общем случае объем, который вы держите в памяти, важнее, поэтому, если вы заполняете буфер (поток) вверх и не перемещаете его, это плохо. Скажем, если бы мы убрали OutputStream из уравнения и просто заполнили InputStream циклом while? Поскольку я тоже это видел, это было бы так же плохо, как и второй пример? - person tigerswithguitars; 28.11.2012
comment
Все зависит от вашего конкретного случая, от того, что вы хотите сделать. Есть алгоритмы, которые могут работать с небольшими порциями (вычисление суммы значений, нахождение максимума), а есть алгоритмы, которым нужны все данные (например: сортировка). Во втором случае необходимо прочитать все данные. В первом случае — не совсем. - person liori; 28.11.2012
comment
Круто ... так что это зависит от приложения, что было моей главной мыслью, я думаю ... а не от природы самих объектов в большинстве языков. Спасибо :) - person tigerswithguitars; 28.11.2012

Иногда InputStream.Length недействителен для какого-либо источника, например, из сетевого транспорта, или буфер может быть огромным, например, чтение из большого файла. ИМО.

person Healer    schedule 28.11.2012
comment
Это действительно хороший момент... Я не думал о такой возможности. Но это имеет большой смысл, особенно если вы находитесь близко к металлу и читаете буфер, получающий информацию! - person tigerswithguitars; 28.11.2012
comment
+1 ... хотелось бы, чтобы это было + 2. Я хотел принять этот ответ, поскольку вы очень просто изложили то, о чем я даже не думал. Что всегда круто. Но в интересах сообщества SO лучше всего принять ответ, который будет полезен большинству людей. - person tigerswithguitars; 28.11.2012

Это защищает вас от ситуации, когда ваш входной поток имеет длину в несколько гигабайт.

person Joe    schedule 28.11.2012
comment
Что вы подразумеваете под защитой? Зачем вам это нужно? - person tigerswithguitars; 28.11.2012
comment
Защита, например, от OutOfMemoryException. - person Joe; 28.11.2012
comment
Верно. Если вы читаете файл, скажем, в память, которая была больше, чем было доступно приложению. Данный. Но это возможно с любым большим объемом данных. Таким образом, фрагментарность шаблона не защищает это, просто сброс буфера в выходной поток и повторное использование. - person tigerswithguitars; 28.11.2012
comment
@tigerswithguitars - Таким образом, массивность шаблона не защищает это - конечно, защищает. В вашем примере вам всегда нужен буфер настроенного размера (4096), а не буфер, размер которого равен размеру входного потока. - person Joe; 28.11.2012
comment
Круто, я понимаю ... но у вас будет только часть всего файла, который вы хотели, верно? Так что вам придется заняться реальной разработкой программного обеспечения и убедиться, что это не приведет к краху мира (прим. ред. — приложение умрет)! :П - person tigerswithguitars; 28.11.2012
comment
@tigerswithguitars, но у вас будет только часть всего файла, который вы хотите, правильно - нет, вы будете копировать весь файл из входного потока в выходной поток по частям за раз. Кстати, с .NET 4 или более поздней версии вы можете просто использовать InputStream.CopyTo(OutputStream), который использует размер буфера по умолчанию, или, например, InputStream.CopyTo(OutputStream, 4096), который позволяет указать размер буфера. - person Joe; 28.11.2012

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

Если вы контролируете ввод и уверены, что размер является разумным, то вы, безусловно, можете сразу прочитать весь массив. Но будьте особенно осторожны, если пользователь может указать произвольный ввод.

person Jon B    schedule 28.11.2012