Pipelines
我们从stream流说起,tream的API 相对来说,大家都很熟悉。
流有些什么问题呢?模糊:在不同场景里使用不同的工作模式:
-
有时时只读,有时只写,有时又是读写同时。
-
即使同一个情况下,都有可能一会只读,一会儿只写(比如:DeflateStream)
-
在一些双重流(NetworkStream,SslStream)情况下,几乎不可能知道数据何时终止传输了,只能不断的使用循环处理。
-
流提供的定位操作也不同,有的是length,有的是Position
-
大量的数据拷贝,内存分配。 有鉴如此,pipelines 在.NET Core 2.1 加入。
pipelines是什么 pipelines可以理解为提供了一组二进制流访问的API,包含内存管理(内存的合并、回收),流控(积压与防溢出),多线程管理。
-
Stream
12345678910111213141516171819202122232425262728using
(MemoryStream ms =
new
MemoryStream())
{
WriteSomeData(ms);
ms.Position = 0;
ReadSomeData(ms);
}
void
WriteSomeData(Stream stream)
{
byte
[] bytes = Encoding.ASCII.GetBytes(
"hello, world!"
);
stream.Write(bytes, 0, bytes.Length);
stream.Flush();
}
void
ReadSomeData(Stream stream)
{
int
bytesRead;
byte
[] buffer =
new
byte
[256];
do
{
bytesRead = stream.Read(buffer, 0, buffer.Length);
if
(bytesRead > 0)
{
string
s = Encoding.ASCII.GetString(buffer, 0, bytesRead);
Console.Write(s);
}
}
while
(bytesRead > 0);
}
-
Pipelines
12345678910111213141516171819202122232425262728293031323334353637async Task ProcessPipe()
{
Pipe pipe =
new
Pipe();
await WriteSomeDataAsync(pipe.Writer);
pipe.Writer.Complete();
await ReadSomeDataAsync(pipe.Reader);
}
async ValueTask WriteSomeDataAsync(PipeWriter writer)
{
Memory<
byte
> workspace = writer.GetMemory(512);
int
bytes = Encoding.ASCII.GetBytes(
"hello, world!"
, workspace.Span);
writer.Advance(bytes);
await writer.FlushAsync();
}
async ValueTask ReadSomeDataAsync(PipeReader reader)
{
while
(
true
)
{
ReadResult read = await reader.ReadAsync();
ReadOnlySequence<
byte
> buffer = read.Buffer;
if
(buffer.IsEmpty && read.IsCompleted)
break
;
if
(buffer.IsSingleSegment)
{
Encoding.ASCII.GetString(buffer.First);
}
else
{
foreach
(Memory<
byte
> segment
in
buffer)
{
string
s = Encoding.ASCII.GetString(segment.Span);
Console.Write(s);
}
}
reader.AdvanceTo(buffer.End);
}
}
pipeline 提供了两个独立的读写API处理读写问题。
pipewriter:
处理写操作,pipeline使用GetMemory获取内存区替代显示分配byte[] 。然后向管道写入数据。这里并不是只能使用512个字节,可以零字节,也可以一万字节,通过Advance(bytes) 完成单次的数据写入,同时告知管道有多少数据可以被reader消费。之后Flush管道。
数据写入完成之后,调用complete方法,通知管道写入操作结束。
pipereader:
read.IsCompleted
表示writer是否执行complete。
buffer.IsEmpty
告诉我们单前迭代种管道是否已经没有数据。
AdvanceTo
告诉管道我们已经消费了多少数据。
另外
上面的列子我们使用AdvanceTo(buffer.End),把一切都消费掉了。更一般的情况可能是自定义的数据的格式或者通过 cr/lf
来处理数据隔断等,即可能一次消费的长度是小于521或者大于512字节。 ReadOnlySequence<byte>
-
Slice()
方法通过切片可以返回我们真实需要处理的数据。 -
GetPosition()
获取相对处理的位置。 -
reader.AdvanceTo(consumedToPosition, buffer.End)
告诉管道我们已经消费了多少位置,同时检查了所有数据,对于消费点到结束点数据我们不能处理,我们需要更多的数据来处理。
回过头在看看PipeWriter.FlushAsync()
和PipeReader.ReadAsync()。
他们之间有微妙的平衡。
-
ReadAsync
表示管道需要数据,会唤醒reader,执行读取循环。 -
作者:RunStone
出处:https://www.cnblogs.com/RunStone/p/11161581.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构