Overview

PipeWriter manages buffers for writing on the caller’s behalf. It implements IBufferWriter<byte> to get access to buffers to perform writes without extra buffer copies:

async Task WriteHelloAsync(PipeWriter writer, CancellationToken cancellationToken = default)
{
    // Request a buffer of at least 5 bytes from the PipeWriter.
    Memory<byte> memory = writer.GetMemory(5);

    // Write directly into the buffer.
    int written = Encoding.ASCII.GetBytes("Hello".AsSpan(), memory.Span);

    // Tell the writer how many bytes were written.
    writer.Advance(written);

    // Send the bytes to the underlying device.
    await writer.FlushAsync(cancellationToken);
}

Using PipeWriter.WriteAsync instead of the buffers provided by PipeWriter is easier. WriteAsync calls GetSpan and Advance as needed and finishes by calling FlushAsync:

async Task WriteHelloAsync(PipeWriter writer, CancellationToken cancellationToken = default)
{
    byte[] helloBytes = Encoding.ASCII.GetBytes("Hello");

    // Write helloBytes to the writer; there's no need to call Advance here (Write does that).
    await writer.WriteAsync(helloBytes, cancellationToken);
}

Cancellation with PipeWriter

PipeWriter.FlushAsync accepts a CancellationToken. However, PipeWriter.CancelPendingFlush cancels the flush operation without raising an exception. It causes the current or next call to FlushAsync or WriteAsync to return a FlushResult with IsCanceled set to true.

Common Problems with PipeWriter

See this page for more information.