Можете ли вы объяснить концепцию потоков?


186

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

По какой-то причине эта концепция просто не подходит для меня. Я прочитал кучу статей, но я думаю, что мне нужна аналогия или что-то.

Ответы:


234

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

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

Тем не менее, как только вы начинаете думать, что вам нужны только те данные, которые вам нужны, независимо от того, откуда они берутся, абстракции, о которых говорили другие, становятся более ясными. Вы начинаете думать, что можете обернуть потоки, и ваши методы все равно будут работать идеально. Например, вы можете сделать это:

int ReadInt(StreamReader reader) { return Int32.Parse(reader.ReadLine()); }

// in another method:
Stream fileStream = new FileStream("My Data.dat");
Stream zipStream = new ZipDecompressorStream(fileStream);
Stream decryptedStream = new DecryptionStream(zipStream);
StreamReader reader = new StreamReader(decryptedStream);

int x = ReadInt(reader);

Как видите, становится очень легко изменить источник входного сигнала без изменения логики обработки. Например, чтобы прочитать ваши данные из сетевого сокета вместо файла:

Stream stream = new NetworkStream(mySocket);
StreamReader reader = new StreamReader(stream);
int x = ReadInt(reader);

Так легко, как это может быть. И красота продолжается, так как вы можете использовать любой источник ввода, если вы можете создать для него потоковую «обертку». Вы могли бы даже сделать это:

public class RandomNumbersStreamReader : StreamReader {
    private Random random = new Random();

    public String ReadLine() { return random.Next().ToString(); }
}

// and to call it:
int x = ReadInt(new RandomNumbersStreamReader());

Видеть? Пока ваш метод не заботится о том, что является источником ввода, вы можете настроить его различными способами. Абстракция позволяет очень элегантно отделить ввод от логики обработки.

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

Итак, подведем итог: поток - это просто источник ввода, скрывающий (абстрагирующий) другой источник. Пока вы не нарушите абстракцию, ваш код будет очень гибким.


6
Абстрактное мышление (и объяснение), кажется, у вас в крови;) Ваша аналогия с водой (и, следовательно, метафорические ссылки) напомнила мне Омара Хайяма.
java.is.for.desktop

@HosamAly Ваше объяснение очень ясно, но что-то немного смущает меня в примере кода. Явное преобразование из строки в int выполняется автоматически, делая ReadInt? я полагаю, я мог бы сделать ReadString тоже?
Рушино

1
@Rushino В приведенном выше коде нет конверсий. Метод ReadIntопределен в самом верху используя метод int.Parse, который получает возвращаемую строку reader.ReadLine()и анализирует ее. Конечно, вы можете создать аналогичный ReadStringметод. Это достаточно ясно?
Хосам Али

Хорошо сказано. Для меня потоки - самые простые и мощные общие абстракции во всем программировании. Наличие .net basic Stream.Copyзначительно облегчает жизнь во многих приложениях.
Felype

38

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

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

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


Разве разные типы потоковых ридеров, использованных в приведенном выше примере @HosamAly, не подразумевают, что вы знаете, что такое резервное хранилище? Я так понимаю, FileStream, NetworkStream и т.д ... читают из этих типов источников. Кроме того, есть ли случаи, когда вы не знаете, каким может быть резервное хранилище, которое будет динамически выбираться во время работы программы? Я просто лично не сталкивался с этим и хотел бы знать больше.
user137717

Кроме того, могут ли потоки данных передаваться через какой-либо процесс по мере генерирования данных, или мне нужен доступ к полному набору данных, с которым я хочу работать, когда начинаю процесс?
user137717

@ user137717: Нет, если вы просто возьмете StreamReader- или лучше, TextReaderто ваш код не знает, какой поток лежит в основе потока данных. Или, скорее, он может использовать BaseStreamсвойство для определения типа - но это может быть тип, которого ваш код никогда не видел раньше. Дело в том, что вам все равно. И да, вы можете абсолютно писать код , который будет иногда использоваться для сетевого потока , и иногда можно использовать для файлового потока. Что касается потоков, передающих данные через процесс - ну, это не будет сделано внутри процесса ... это будет поставщик потоков.
Джон Скит

30

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


Да, это позволяет вам менять тип потока, не нарушая ваш код. Например, вы можете прочитать файл из одного вызова, а затем из буфера памяти - из следующего.
Крэйг,

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

11

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


7

Чтобы добавить в эхо-камеру, поток - это абстракция, поэтому вам нет дела до основного хранилища. Это имеет смысл, когда вы рассматриваете сценарии с потоками и без них.

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

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

Допустим, у меня есть какой-то объект TcpDataStream. Я создаю его с соответствующей информацией о соединении, затем читаю байты из потока, пока не будет сказано, что байтов больше нет. Поток обрабатывает управление буфером, условия окончания данных и управление соединением.

Таким образом, потоки облегчают ввод / вывод. Конечно, вы могли бы написать класс TcpFileDownloader, который делает то же, что и поток, но тогда у вас есть класс, специфичный для TCP. Большинство потоковых интерфейсов просто предоставляют методы Read () и Write (), а любые более сложные концепции обрабатываются внутренней реализацией. Из-за этого вы можете использовать один и тот же базовый код для чтения или записи в память, дисковые файлы, сокеты и многие другие хранилища данных.


5

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

У вас есть простые компоненты, которые делают одно, например, устройство для нанесения вишни на торт. Это устройство имеет входной поток тортов без вишни и выходной поток тортов с вишней. Есть три преимущества, о которых стоит упомянуть, структурируя вашу обработку таким образом.

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

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

В-третьих, устройствам не нужно управлять инвентарем, упаковкой или распаковкой. Самый эффективный способ агрегирования и упаковки вещей может быть изменен: возможно, сегодня вы складываете свои пирожные в коробки по 48 штук и отправляете их на грузовиках, но завтра вы захотите разослать коробки по шесть в ответ на индивидуальные заказы. Такое изменение может быть осуществлено путем замены или перенастройки машин в начале и в конце производственной линии; Вишневая машина в середине строки не должна изменяться для обработки различного количества элементов за раз, она всегда работает с одним элементом за раз, и ей не нужно знать, каков ее ввод или вывод будучи сгруппированным.


Отличный пример аналогии как объяснение.
Ричи Томас,

5

Когда я впервые услышал о потоковой передаче, это было в контексте прямой трансляции с веб-камеры. Итак, один хост транслирует видео контент, а другой хост получает видео контент. Так это потоковое? Ну ... да ... но прямой эфир - это конкретная концепция, и я думаю, что этот вопрос относится к абстрактной концепции потоковой передачи. Смотрите https://en.wikipedia.org/wiki/Live_streaming

Итак, давайте двигаться дальше.


Видео - не единственный ресурс, который можно транслировать. Аудио также может передаваться в потоковом режиме. Итак, мы сейчас говорим о потоковом медиа. Смотрите https://en.wikipedia.org/wiki/Streaming_media . Аудио может быть доставлено от источника к цели множеством способов. Итак, давайте сравним некоторые методы доставки данных друг с другом.

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

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

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

Потоковый сервер поддерживает двустороннее соединение со своим клиентом, а веб-сервер закрывает соединение после ответа сервера.


Аудио и видео не единственное, что можно транслировать. Давайте посмотрим на концепцию потоков в руководстве по PHP.

Поток - это ресурсный объект, который демонстрирует поведение в потоке. То есть он может считываться или записываться линейным образом и может быть способен выполнять fseek () в произвольном месте в потоке. Ссылка: https://www.php.net/manual/en/intro.stream.php

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

Пример текстового файла, который подвергается потоковой передаче:

// Let's say that cheese.txt is a file that contains this content: 
// I like cheese, a lot! My favorite cheese brand is Leerdammer.
$fp = fopen('cheese.txt', 'r');

$str8 = fread($fp, 8); // read first 8 characters from stream. 

fseek($fp, 21); // set position indicator from stream at the 21th position (0 = first position)
$str30 = fread($fp, 30); // read 30 characters from stream

echo $str8; // Output: I like c 
echo $str30; // Output: My favorite cheese brand is L

Zip-файлы также могут быть потоковыми. Кроме того, потоковая передача не ограничивается файлами. HTTP, FTP, SSH соединения и ввод / вывод также могут быть потоковыми.


Что википедия говорит о концепции потоковой передачи?

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

Смотрите: https://en.wikipedia.org/wiki/Stream_%28computing%29 .

Википедия ссылается на это: https://srfi.schemers.org/srfi-41/srfi-41.html, и авторы могут сказать следующее о потоках:

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

Таким образом, поток на самом деле является структурой данных.


Мой вывод: поток - это источник, который может содержать данные, которые можно читать или записывать в последовательном порядке. Поток не читает сразу все, что содержит источник, он читает / записывает последовательно.


Полезные ссылки:

  1. http://www.slideshare.net/auroraeosrose/writing-and-using-php-streams-and-sockets-zendcon-2011 Обеспечивает очень четкое представление
  2. https://www.sk89q.com/2010/04/introduction-to-php-streams/
  3. http://www.netlingo.com/word/stream-or-streaming.php
  4. http://www.brainbell.com/tutorials/php/Using_PHP_Streams.htm
  5. http://www.sitepoint.com/php-streaming-output-buffering-explained/
  6. http://php.net/manual/en/wrappers.php
  7. http://www.digidata-lb.com/streaming/Streaming_Proposal.pdf
  8. http://www.webopedia.com/TERM/S/streaming.html
  9. https://en.wikipedia.org/wiki/Stream_%28computing%29
  10. https://srfi.schemers.org/srfi-41/srfi-41.html

4

Это просто концепция, еще один уровень абстракции, который делает вашу жизнь проще. И все они имеют общий интерфейс, что означает, что вы можете комбинировать их как в трубе. Например, кодировать в base64, затем архивировать, а затем записать это на диск и все в одну строку!


Это, конечно, полезно, но я бы не сказал, что в этом вся суть. Даже без цепочки полезно иметь общую абстракцию.
Джон Скит

Да, ты прав. Я изменил слова, чтобы прояснить это.
vava

Да, это лучше. Надеюсь, ты не думал, что я слишком привередлива!
Джон Скит

3

Лучшее объяснение потоков, которые я видел, это глава 3 SICP . (Возможно, вам придется прочитать первые 2 главы, чтобы это имело смысл, но в любом случае вам следует. :-)

Они вообще не используют стерамы для байтов, а целые числа. Большие моменты, которые я получил от этого, были:

  • Потоки списков с задержкой
  • Затраты на вычислительные ресурсы [в некоторых случаях нетерпеливого вычисления всего заранее) возмутительны
  • Мы можем использовать потоки для представления последовательностей, которые являются бесконечно длинными

Я на самом деле в настоящее время на главе 1 SICP. Спасибо!
Роб Соберс

2
один хотел бы сказать поток SICP от других. Важная особенность SICP потока является лень , в то время как общий поток концепция подчеркивает абстракции на последовательностях данных .
道 嘉 道

2

Еще один момент (для чтения файла ситуации):

  1. stream может позволить вам сделать что-то еще, прежде чем finished reading all content of the file .
  2. Вы можете сэкономить память, потому что не нужно загружать все содержимое файла одновременно.

1

Думайте о потоках как об абстрактном источнике данных (байты, символы и т. Д.). Они абстрагируют реальную механику чтения и записи в конкретный источник данных, будь то сетевой сокет, файл на диске или ответ от веб-сервера.


1

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

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

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


0

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

Например, если вы обрабатываете данные через поток, то для вашего кода не имеет значения, поступают ли данные из файла, сетевого подключения, строки, большого двоичного объекта в базе данных и т. Д. И т. Д. И т. Д.

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


0

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

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


0

Я буду кратким, я просто пропустил слово здесь:

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

(Теперь, поскольку мы все знаем, что такое очереди, нет необходимости объяснять это дальше.)

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.