1. 程式人生 > >【5min+】傳說中的孿生兄弟? Memory and Span

【5min+】傳說中的孿生兄弟? Memory and Span

系列介紹

【五分鐘的dotnet】是一個利用您的碎片化時間來學習和豐富.net知識的博文系列。它所包含了.net體系中可能會涉及到的方方面面,比如C#的小細節,AspnetCore,微服務中的.net知識等等。
5min+不是超過5分鐘的意思,"+"是知識的增加。so,它是讓您花費5分鐘以下的時間來提升您的知識儲備量。

正文

在上一篇文章:《閃電光速拳? .NetCore 中的Span》 中我們提到了在.net core 2.x 所新增的一個型別:Span。

它與咱們傳統使用的基礎型別相比具有超高的效能,原因是它減少了大量的記憶體分配和資料量複製,並且它所分配的資料記憶體是連續的。

但是您會發現它無法用在我們專案的某些地方,它獨特的 ref結構 使它沒有辦法跨執行緒使用、更沒有辦法使用Lambda表示式。

特別是在AspNetCore中,咱們會使用到大量的非同步操作方法。“所以,這個時候如果我們又想跨執行緒操作資料又想獲得類似Span這樣的效能怎麼辦呢?” 上一篇文章我們留下了這樣的一個問題,所以現在就是到了還願的時候了。它就是與Span一起釋出的孿生兄弟: Memory。

獅子座和射手座黃金聖鬥士同樣具備超越光速的能力

什麼是Memory

那什麼是Memory呢?不妨我們先來猜測一下,它的結構是什麼樣子。畢竟它是Span的孿生兄弟,而Span的結構我們在前面就瞭解過了:

public readonly ref struct Span<T>
{
    public void Clear();
    public void CopyTo([NullableAttribute(new[] { 0, 1 })] Span<T> destination);
    public void Fill(T value);
    public Enumerator GetEnumerator();
    public Span<T> Slice(int start, int length);
    public T[] ToArray();
    public override string ToString();

    //.....
}

當時我們說Span有各種缺陷的原因是由於它獨特的 ref struct 關鍵字所導致的,導致它無法拆箱裝箱、無法書寫Lambda、無法跨執行緒等。但是它兄弟卻可以克服缺點,所以我們想想它會和Span在宣告上有哪些差距呢? 是的,您可能已經想到了:它不會有 ref 關鍵字了。

所以,我們看到它的內部結構就是醬紫的:

public readonly struct Memory<T>
{
    public static Memory<T> Empty { get; }
    public bool IsEmpty { get; }
    public int Length { get; }
    public Span<T> Span { get; }
    public void CopyTo([NullableAttribute(new[] { 0, 1 })] Memory<T> destination);
    public MemoryHandle Pin();
    public Memory<T> Slice(int start, int length);
    public T[] ToArray();
    public override string ToString();
}

和我們猜想的一樣。它少了ref關鍵字,內部方法也和Span差不多(同樣擁有CopyTo,Slice等),但是還是有一些差異,比如多了Pin方法,Span屬性等。

被宣告為ref struct的結構,叫做“ByRefLike”。所以在我們在進行反射的時候,我們使用Type會看到有這樣一個屬性:IsByRefLike。

好像有點超綱了哈(>人<;)

按照MSDN給出的解釋:

該結構是使用中的C# ref struct 關鍵字宣告的。 不能將類似 byref 的結構的例項放置在託管堆上。

所以這也是為什麼上一篇文章說的:Span只能放置在記憶體棧中的原因。

那麼反過來想,沒有了ref關鍵字之後。Memory是不是就可以放置在託管堆上了呢?是不是就可以進行拆裝箱,克隆副本供其它執行緒的記憶體棧使用了呢? 好吧,可能是這樣。所以這也許就是它能夠被允許跨執行緒使用的原因吧。

進行到了這一步,那我們再回過頭來想想Memory是什麼呢? 其實現在我們心裡其實都已經有個底了:

與 Span<T>一樣,Memory<T> 表示記憶體的連續區域。 但 Span<T>不同,Memory<T> 不是ref 結構。 這意味著 Memory<T> 可以放置在託管堆上,而 Span<T> 不能。 因此,Memory<T> 結構與 Span<T> 例項沒有相同的限制。 具體而言:

  • 它可用作類中的欄位。
  • 它可跨 await 和 yield 邊界使用。

除了 Memory<T>之外,還可以使用 System.ReadOnlyMemory<T> 來表示不可變或只讀記憶體。

這是MSDN給出來的解釋,不是我亂編的哈