立即返回流,然后异步写入流

时间:2017-09-22 09:46:16

标签: c# asynchronous stream streamwriter memorystream

在我目前的代码中,我有一个这样的方法来读取设备中的数据(伪代码):

public async Task<string> ReadAllDataFromDevice()
{
    var buffer = "";
    using (var device = new Device())
    {
        while(device.HasMoreData)
        {
            buffer += await device.ReadLineAsync();
        }
    }
    return buffer;
}

然后我想通过网络将所有数据发送给某个接收器。数据量可能非常大。很明显,上述设计的内存效率不高,因为它需要在我开始将数据发送到网络套接字之前读取所有数据。

所以我想要的是一个返回流的函数。像这样:

public async Task<Stream> ReadAllDataFromDevice()
{
    var stream = new MemoryStream();
    using (var device = new Device())
    using (var streamWriter = new StreamWriter(stream, new UTF8Encoding(), 512, true))
    {
        while(device.HasMoreData)
        {
            var line = await device.ReadLineAsync();
            await streamWriter.WriteLineAsync(line);
        }
        await streamWriter.FlushAsync();
    }
    return stream;
}

这会返回一个流,但它显然无法解决我的问题,因为只有在从设备读取所有数据后才返回流。

所以我想出了这个:

public Stream ReadAllDataFromDevice()
{
    var stream = new MemoryStream();
    Task.Run(async () => {
        using (var device = new Device())
        using (var streamWriter = new StreamWriter(stream, new UTF8Encoding(), 512, true))
        {
            while(device.HasMoreData)
            {
                var line = await device.ReadLineAsync();
                await streamWriter.WriteLineAsync(line);
            }
            await streamWriter.FlushAsync();
        }
    });
    return stream;
}

这是一个好设计吗?我特别关注线程安全性,lambda中使用的流对象的生命周期以及异常处理。

或者这种问题有更好的模式吗?

修改

实际上我只想出了另一种看起来更干净的设计。我没有让ReadAllDataFromDevice()函数返回流,而是让数据的使用者提供流,如下所示:

public async Task ReadAllDataFromDevice(Stream stream)
{
    using (var device = new Device())
    using (var streamWriter = new StreamWriter(stream, new UTF8Encoding(), 512, true))
    {
        while(device.HasMoreData)
        {
            var line = await device.ReadLineAsync();
            await streamWriter.WriteLineAsync(line);
        }
        await streamWriter.FlushAsync();
    }
}

1 个答案:

答案 0 :(得分:1)

这是我现在使用的设计:

public async Task ReadAllDataFromDevice(Func<Stream, Task> readCallback)
{
    using (var device = new Device())
    {
        await device.Initialize();
        using (var stream = new DeviceStream(device))
        {
            await readCallback(stream);
        }
    }
}

逐行设备访问封装在自定义DeviceStream类中(此处未显示)。

数据的消费者看起来像这样:

await ReadAllDataFromDevice(async stream => {
    using (var streamReader(stream))
    {
        var data = await streamReader.ReadToEndAsync();
        // do something with data
    }
});