溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C#的高效IO庫System.IO.Pipelines怎么使用

發(fā)布時間:2022-07-02 13:45:29 來源:億速云 閱讀:275 作者:iii 欄目:開發(fā)技術(shù)

今天小編給大家分享一下C#的高效IO庫System.IO.Pipelines怎么使用的相關(guān)知識點,內(nèi)容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

數(shù)據(jù)讀不全:

可能不能在一次read操作中讀入所有需要的數(shù)據(jù),因此需要在緩沖區(qū)中維護一個游標,記錄下次讀取操作的起始位置,這個游標帶了了不小的復雜度:

  • 從緩沖區(qū)讀數(shù)據(jù)時,要根據(jù)游標計算緩沖區(qū)起始寫位置,以及剩余空間大小。增加了讀數(shù)據(jù)的復雜度。

  • 解析數(shù)據(jù)也是復用這個緩沖區(qū)的,解析的時候也要判斷游標起始位置,剩余空間大小。同時增加了解析數(shù)據(jù)的復雜度。

  • 解析玩了后還要移動游標,重新標記緩沖區(qū)起始位置,再次增加了復雜度。

緩沖區(qū)容量有限:

由于緩沖區(qū)有限,可能申請的緩沖區(qū)不夠用,需要引入動態(tài)緩沖區(qū)。這也大幅加大了代碼的復雜度。

  • 如果每次都申請更大的內(nèi)存,一方面帶來的內(nèi)存申請釋放開銷,另一方面需要將原來的數(shù)據(jù)移動,并更新游標,帶來更復雜的邏輯。

  • 如果靠多段的內(nèi)存組成一個邏輯整理,數(shù)據(jù)的讀寫方式都比較復雜。

  • 使用完后的內(nèi)存要釋放,如果需要更高的效率還要維持一個內(nèi)存池。

讀和用沒有分離

我們的業(yè)務本身只關(guān)心使用操作,但讀和用操作沒有分離,復雜的都操作導致用操作也變得復雜,并且嚴重干擾業(yè)務邏輯。

今天介紹微軟新推出的一個庫:System.IO.Pipelines(需要在Nuget上安裝),用于解決這些痛點。它主要包含一個Pipe對象,它有一個Writer屬性和Reader屬性。

var pipe   = new Pipe();
var writer = pipe.Writer;
var reader = pipe.Reader;

Writer對象

Writer對象用于從數(shù)據(jù)源讀取數(shù)據(jù),將數(shù)據(jù)寫入管道中;它對應業(yè)務中的"讀"操作。

var content = Encoding.Default.GetBytes("hello world");
var data    = new Memory<byte>(content);
var result  = await writer.WriteAsync(data);

另外,它也有一種使用Pipe申請Memory的方式

var buffer = writer.GetMemory(512);
content.CopyTo(buffer);
writer.Advance(content.Length);
var result = await writer.FlushAsync();

Reader對象

Reader對象用于從管道中獲取數(shù)據(jù)源,它對應業(yè)務中的"用"操作。

首先獲取管道的緩沖區(qū):

var result = await reader.ReadAsync();
var buffer = result.Buffer;

這個Buffer是一個ReadOnlySequence<byte>對象,它是一個相當好的動態(tài)內(nèi)存對象,并且相當高效。它本身由多段Memory<byte>組成,查看Memory段的方法有:

  • IsSingleSegment: 判斷是否只有一段Memory<byte>

  • First: 獲取第一段Memory<byte>

  • GetEnumerator: 獲取分段的Memory<byte>

它從邏輯上也可以看成一段連續(xù)的Memory<byte>,也有類似的方法:

  • Length: 整個數(shù)據(jù)緩沖區(qū)長度

  • Slice: 分割緩沖區(qū)

  • CopyTo: 將內(nèi)容復制到Span中

  • ToArray: 將內(nèi)容復制到byte[]中

另外,它還有一個類似游標的位置對象SequencePosition,可以從其Position相關(guān)函數(shù)中使用,這里就不多介紹了。

這個緩沖區(qū)解決了"數(shù)據(jù)讀不夠"的問題,一次讀取的不夠下次可以接著讀,不用緩沖區(qū)的動態(tài)分配,高效的內(nèi)存管理方式帶來了良好的性能,好用的接口是我們能更關(guān)注業(yè)務。

獲取到緩沖區(qū)后,就是使用緩沖區(qū)的數(shù)據(jù)

var data = buffer.ToArray();

使用完后,告訴PIPE當前使用了多少數(shù)據(jù),下次接著從結(jié)束位置后讀起

reader.AdvanceTo(buffer.GetPosition(4));

這是一個相當實用的設計,它解決了"讀了就得用"的問題,不僅可以將不用的數(shù)據(jù)下次再使用,還可以實現(xiàn)Peek的操作,只讀但不改變游標。

交互

除了"讀"和"用"操作外,它們之間還需要一些交互,例如:

  • 讀過程中數(shù)據(jù)源不可用,需要停止使用

  • 使用過程中業(yè)務結(jié)束,需要中止數(shù)據(jù)源。

Reader和Writer都有一個Complete函數(shù),用于通知結(jié)束:

reader.Complete();
writer.Complete();

在Writer寫入和Reader讀取時,會獲得一個結(jié)果

FlushResult result = await writer.FlushAsync();
ReadResult result = await reader.ReadAsync();

它們都有一個IsComplete屬性,可以根據(jù)它是否為true判斷是否已經(jīng)結(jié)束了讀和寫的操作。

取消

在寫入和讀取的時候,也可以傳入一個CancellationToken,用于取消相應的操作。

writer.FlushAsync(CancellationToken.None);
reader.ReadAsync(CancellationToken.None);

如果取消成功,對應的Result的IsCanceled則為true(沒有驗證過)

以上就是“C#的高效IO庫System.IO.Pipelines怎么使用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

io
AI