Partager via


SpanOwner<T>

SpanOwner<T> constitue un type de tampon à pile uniquement qui loue des tampons à partir d’un pool de mémoire partagé. Il met essentiellement en mémoire la fonctionnalité de MemoryOwner<T>, mais comme type ref struct. Ce type d’opération est particulièrement utile pour des mémoires de courte durée uniquement utilisées dans du code synchrone (qui ne nécessitent aucune instance Memory<T>), ainsi que dans du code s’exécutant dans une boucle serrée, comme la création de valeurs SpanOwner<T> qui n’exigent aucune allocation de mémoire.

API de plateforme : SpanOwner<T>, MemoryOwner<T>

Syntaxe

Les mêmes fonctionnalités de base de MemoryOwner<T> s’appliquent également à ce type, excepté s’il s’agit d’un struct de pile uniquement et si l’implémentation de l’interface IMemoryOwner<T> y est manquante, ainsi que la propriété Memory<T>. La syntaxe est virtuellement identique à ce qui est également utilisé avec MemoryOwner<T>, à l’exception des différences précédemment mentionnées.

Par exemple, supposons que nous avons une ,méthode dans laquelle nous devons allouer un tampon temporaire de taille spécifiée (appelons cette valeur length) que nous utilisons ensuite pour effectuer certains travaux. Il est possible qu’une première version inefficace ressemble à ceci :

byte[] buffer = new byte[length];

// Use buffer here

Cette opération n’est pas optimale, car nous allouons un nouveau tampon lors de chaque utilisation de ce code, puis nous le supprimons immédiatement (comme indiqué également dans la documentation MemoryOwner<T>), ce qui crée davantage de pression sur le récupérateur de mémoire. Nous pouvons optimiser le code ci-dessus en utilisant ArrayPool<T> :

// Using directive to access the ArrayPool<T> type
using System.Buffers;

int[] buffer = ArrayPool<int>.Shared.Rent(length);

try
{
    // Slice the span, as it might be larger than the requested size
    Span<int> span = buffer.AsSpan(0, length);

    // Use the span here
}
finally
{
    ArrayPool<int>.Shared.Return(buffer);
}

Le code ci-dessus loue un tampon d’un pool de tableaux, mais il est plus détaillé et générateur d’erreurs : nous devons être prudents avec le bloc try/finally pour veiller à toujours retourner le tampon loué au pool. Nous pouvons le réécrire en utilisant le type SpanOwner<T>, comme suit :

// Be sure to include this using at the top of the file:
using Microsoft.Toolkit.HighPerformance.Buffers;

using SpanOwner<int> buffer = SpanOwner<int>.Allocate(length);

Span<int> span = buffer.Span;

// Use the span here, no slicing necessary

L’instance SpanOwner<T> va louer un tableau en interne et s’occuper de le retourner au pool lorsqu’il est hors de portée. Nous n’avons plus besoin d’utiliser un bloc try/finally, car le compiler C# va l’ajouter automatiquement lors du développement de cette instruction using. Ainsi, le type SpanOwner<T> peut être considéré comme un wrapper léger autour des API ArrayPool<T>, ce qui les rend plus compactes et faciles à utiliser et réduit ainsi la quantité de code nécessaire à écrire pour louer et supprimer correctement des mémoires de courte durée. Vous pouvez voir comment l’utilisation de SpanOwner<T> rend le code beaucoup plus court et plus direct.

Remarque

Étant donné qu’il s’agit d’un type à pile uniquement, il s’appuie sur le modèle IDisposable duck-typed (à typage canard) présenté dans C# 8. Il est présenté dans l’exemple ci-dessus : le type SpanOwner<T> est utilisé au sein d’un bloc using malgré le fait que le type n’implémente pas du tout l’interface IDisposable et qu’il ne soit jamais encadré. La fonctionnalité est simplement la même : dès que le tampon est hors de portée, il est automatiquement supprimé. Les API dans SpanOwner{T} s’appuient sur ce modèle pour bénéficier de performances supplémentaires : elles supposent que le tampon sous-jacent ne va jamais être supprimé aussi longtemps que le type SpanOwner<T> reste dans l’étendue. Elles n’effectuent pas les vérifications supplémentaires réalisées dans MemoryOwner<T> pour veiller à ce que le tampon soit toujours disponible avant d’y retourner une Memory<T> ou une instance Span<T>. Ainsi, ce type doit toujours être utilisé avec une expression ou un bloc using. Si vous n’effectuez pas cette action, le tampon sous-jacent ne sera pas retourné au pool partagé. Vous pouvez techniquement effectuer la même opération en appelant manuellement Dispose sur le type SpanOwner<T> (qui ne nécessite pas C# 8), mais cette action est sujette à erreurs et donc déconseillée.

Exemples

Vous trouverez d’autres exemples dans les tests unitaires.