Другие ответы здесь великолепны, но я подумал, что может быть полезен тот, который действительно высоко оценивает, для чего служат пары. В приведенном ниже объяснении происходит некоторое упрощение, но, надеюсь, это поможет понять:
Что такое поток?
Поток - это, по сути, поток данных между двумя местами, это скорее канал, а не содержимое этого канала.
Плохая аналогия для начала
Представьте себе опреснительную установку (которая забирает морскую воду, удаляет соль и выводит чистую питьевую воду в водопроводную сеть):
Опреснительная установка не может удалить соль со всего моря за один раз (и при этом мы не хотим, чтобы… где обитала морская рыба?), Поэтому вместо этого мы имеем:
- A
SeaStream
, который всасывает определенное количество воды за раз в растение.
- То, что
SeaStream
связано с DesalinationStream
для удаления соли
- А выход
DesalinationStream
подключен к DrinkingWaterNetworkStream
для подачи теперь бессолевой воды в систему питьевого водоснабжения.
ОК, так какое отношение это имеет к компьютерам?
Перемещение больших файлов одновременно может быть проблематичным
Часто в вычислениях мы хотим перемещать данные между двумя точками, например, с внешнего жесткого диска на двоичное поле в базе данных (чтобы использовать пример, приведенный в другом ответе). Мы можем сделать это, скопировав все данные из файла из местоположения A в память компьютера и оттуда в местоположение B, но если файл большой или источник или место назначения потенциально ненадежны, то перемещение всего файла за один раз может быть невыполнимым или неразумным.
Например, скажем, мы хотим переместить большой файл на USB-накопителе в поле в базе данных. Мы могли бы использовать объект System.IO.File для извлечения всего этого файла в память компьютера, а затем использовать соединение с базой данных для передачи этого файла в базу данных.
Но это потенциально проблематично, что, если файл больше, чем доступная оперативная память компьютера? Теперь файл потенциально будет кэшироваться на жесткий диск, что является медленным, и это может даже замедлить работу компьютера.
Аналогично, что если источник данных ненадежен, например, копирование файла с сетевого диска при медленном и нестабильном соединении WiFi? Попытка скопировать большой файл за один раз может приводить в бешенство, потому что вы получаете половину файла, а затем соединение разрывается, и вам приходится начинать все сначала, только для того, чтобы он потенциально мог снова выйти из строя.
Может быть лучше разбить файл и переместить его по частям за раз
Таким образом, вместо того, чтобы получать весь файл сразу, было бы лучше извлекать файл по частям за раз и передавать каждый кусок по назначению по одному. Это то, что делает Stream
, и вот где появляются два разных типа потоков, о которых вы упомянули:
- Мы можем использовать
FileStream
для извлечения данных из файла по частям за один раз
- и API базы данных может сделать доступной конечную точку
MemoryStream
, которую мы можем записать в кусок за раз.
- Мы соединяем эти два «канала» вместе, чтобы передавать кусочки файла из файла в базу данных.
Даже если файл не был слишком большим для хранения в оперативной памяти, без потоков мы все равно выполняли операции с числами или операции чтения / записи, которые нам не нужны. Этапы, которые мы проводим, были:
- Извлечение данных с диска (медленно)
- Запись в объект File в памяти компьютера (немного быстрее)
- Чтение из этого объекта File в памяти компьютера (снова быстрее)
- Запись в базу данных (вероятно, медленная, поскольку в конце этого канала, возможно, есть вращающийся жесткий диск)
Потоки позволяют нам концептуально покончить с двумя средними этапами, вместо того, чтобы перетаскивать весь файл сразу в память компьютера, мы берем выходные данные операции, чтобы извлечь данные и направить их прямо в операцию, чтобы передать данные набаза данных.
Другие преимущества потоков
Отделение извлечения данных от записи данных также позволяет нам выполнять действия между извлечением данных и их передачей.Например, мы могли бы добавить этап шифрования или записать входящие данные в более чем один тип выходного потока (например, в FileStream и NetworkStream).
Потоки также позволяют нам писать код там, где мыможет возобновить операцию, если передача не пройдена частично.Отслеживая количество фрагментов, которые мы переместили, если передача не удалась (например, если сетевое соединение прерывается), мы можем перезапустить поток с того места, в котором мы получили последний фрагмент (это offset
вBeginRead
метод).