Является ли Stream.Copy каналом?

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

4 ответа

Вот реализация CopyTo в.NET 4.5:

private void InternalCopyTo(Stream destination, int bufferSize)
{
    int num;
    byte[] buffer = new byte[bufferSize];
    while ((num = this.Read(buffer, 0, buffer.Length)) != 0)
    {
        destination.Write(buffer, 0, num);
    }
}

Итак, как вы можете видеть, он читает из источника, а затем записывает в место назначения. Возможно, это можно улучшить;)


РЕДАКТИРОВАТЬ: вот возможная реализация конвейерной версии:

public static void CopyToPiped(this Stream source, Stream destination, int bufferSize = 0x14000)
{
    byte[] readBuffer = new byte[bufferSize];
    byte[] writeBuffer = new byte[bufferSize];

    int bytesRead = source.Read(readBuffer, 0, bufferSize);
    while (bytesRead > 0)
    {
        Swap(ref readBuffer, ref writeBuffer);
        var iar = destination.BeginWrite(writeBuffer, 0, bytesRead, null, null);
        bytesRead = source.Read(readBuffer, 0, bufferSize);
        destination.EndWrite(iar);
    }
}

static void Swap<T>(ref T x, ref T y)
{
    T tmp = x;
    x = y;
    y = tmp;
}

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

Я провел несколько тестов производительности:

  • с помощью MemoryStreams, я не ожидал значительного улучшения, так как он не использует порты завершения ввода-вывода (AFAIK); и действительно, производительность практически идентична
  • используя файлы на разных дисках, я ожидал, что конвейерная версия будет работать лучше, но это не так... на самом деле она немного медленнее (на 5-10%)

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

По мнению Reflector это не так. Такое поведение лучше документировать, потому что оно вводит параллелизм. В общем, это никогда не безопасно. Таким образом, дизайн API, чтобы не "труба" это звук.

Так что это не просто вопрос Stream.Copy быть более или менее умным. Одновременное копирование не является деталью реализации.

Stream.Copy - синхронная операция. Я не думаю, что разумно ожидать, что он будет использовать асинхронное чтение / запись для одновременного чтения и записи.

Я ожидаю, что асинхронная версия (например, RandomAccessStream.CopyAsync) будет использовать одновременное чтение и запись.

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

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

Другие вопросы по тегам