Вопрос

I am creating a stream like class, that inherits from Stream. When I use a StreamWriter class to write the information asynchronously:

using (var sw = new StreamWriter(messageWriter, Encoding.UTF8))
    await sw.WriteAsync(msg);

I can see how StreamWriter goes straight to void Stream.Write(byte[] buffer, int offset, int count) rather than Task Stream.WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken).

Is this a bug or am I missing something?

Это было полезно?

Решение

I spotted the source of confusion.

StreamWriter flushes synchronously by default unless you specifically flush asynchronously.

Stream.Flush is called when closing, and of course, flushing.

This will invoke Write rather than WriteAsync in the messageWriter stream:

using (var sw = new StreamWriter(messageWriter, Encoding.UTF8))
    await sw.WriteAsync("Hi");

The StreamWriter will buffer the "Hi" message, and it will be released to the underlying stream on the Flush invocation synchronously when closing the StreamWriter.

So the asynchronous call to StreamWriter.WriteAsync could have no effect on the underlying stream (because the buffering), and when calling StreamWriter.Close, StreamWriter.Flush is called synchronously, and therefore the Flush method on the underlying stream.

This code also calls Write synchronously:

using (var sw = new StreamWriter(messageWriter, Encoding.UTF8))
{
    sw.AutoFlush = true; // will call Write here to send the UTF8 BOM
    await sw.WriteAsync("Hi"); // Write the actual data 
}

obviously also this this:

using (var sw = new StreamWriter(messageWriter, Encoding.UTF8))
{
    await sw.WriteAsync("Hi"); 
    sw.Flush(); 
}

This will call WriteAsync:

using (var sw = new StreamWriter(messageWriter, Encoding.UTF8))
{
    await sw.WriteAsync("Hi");
    await sw.FlushAsync()
}

I have created a simple class to track how the methods are called:

public class MSWrite : MemoryStream
{
    public override bool CanWrite { get { return true; } }

    public override void Write(byte[] buffer, int offset, int count)
    {
        Console.WriteLine("Write");
    }

    public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
    {
        Console.WriteLine("WriteAsync");
        return Task.Run(() => { });
    }
}

And this test:

Console.WriteLine("Simple");
using(var ms = new MSWrite())
using (StreamWriter sw = new StreamWriter(ms, Encoding.UTF8))
{
    await sw.WriteAsync("Hi");
}
Console.WriteLine();

Console.WriteLine("AutoFlush");
using (var ms = new MSWrite())
using (StreamWriter sw = new StreamWriter(ms, Encoding.UTF8))
{
    sw.AutoFlush = true;
    await sw.WriteAsync("Hi");
}
Console.WriteLine();

Console.WriteLine("Flush async");
using (var ms = new MSWrite())
using (StreamWriter sw = new StreamWriter(ms, Encoding.UTF8))
{
    await sw.WriteAsync("Hi");
    await sw.FlushAsync();
}

Yields:

Simple
Write
Write

AutoFlush
Write
WriteAsync

Flush async
WriteAsync
WriteAsync

The first write is the UTF8 BOM, the second is the actual data.

Другие советы

No, StreamWriter.WriteAsync does in fact call Stream.WriteAsync (assuming you are on .NET 4.5). If you are seeing otherwise, please post a stack trace.

Note that the Stream base class by default will implement WriteAsync as a Write done on a thread pool thread. You'll need to override WriteAsync in your custom stream to change this behavior.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top