Creación de un grupo de objetos mediante ConcurrentBagCreate an object pool by using a ConcurrentBag

En este ejemplo se muestra cómo usar ConcurrentBag<T> para implementar un grupo de objetos.This example shows how to use a ConcurrentBag<T> to implement an object pool. Los grupos de objetos pueden mejorar el rendimiento de la aplicación en situaciones donde se requieren varias instancias de una clase y cuesta mucho crear o destruir la clase.Object pools can improve application performance in situations where you require multiple instances of a class and the class is expensive to create or destroy. Cuando un programa cliente solicita un nuevo objeto, el grupo de objetos primero intenta proporcionar uno que ya se ha creado y devuelto al grupo.When a client program requests a new object, the object pool first attempts to provide one that has already been created and returned to the pool. Si no hay ninguno disponible, solo entonces se crea un nuevo objeto.If none is available, only then is a new object created.

ConcurrentBag<T> se usa para almacenar los objetos porque admite la inserción y eliminación rápidas, especialmente cuando el mismo subproceso agrega y quita elementos.The ConcurrentBag<T> is used to store the objects because it supports fast insertion and removal, especially when the same thread is both adding and removing items. Este ejemplo se podría ampliar aún más para crearse en torno a IProducerConsumerCollection<T>, que implementa la estructura de datos del contenedor, al igual que ConcurrentQueue<T> y ConcurrentStack<T>.This example could be further augmented to be built around a IProducerConsumerCollection<T>, which the bag data structure implements, as do ConcurrentQueue<T> and ConcurrentStack<T>.

Sugerencia

En este artículo se define cómo escribir su propia implementación de un grupo de objetos con un tipo simultáneo subyacente a fin de almacenar objetos para su reutilización.This article defines how to write your own implementation of an object pool with an underlying concurrent type to store objects for reuse. Sin embargo, el tipo Microsoft.Extensions.ObjectPool.ObjectPool<T> ya existe en el espacio de nombres Microsoft.Extensions.ObjectPool.However, the Microsoft.Extensions.ObjectPool.ObjectPool<T> type already exists under the Microsoft.Extensions.ObjectPool namespace. Considere la posibilidad de usar el tipo disponible antes de crear su propia implementación, que incluye muchas características adicionales.Consider using the available type before creating your own implementation, which includes many additional features.

EjemploExample

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

namespace ObjectPoolExample
{
    public class ObjectPool<T>
    {
        private readonly ConcurrentBag<T> _objects;
        private readonly Func<T> _objectGenerator;

        public ObjectPool(Func<T> objectGenerator)
        {
            _objectGenerator = objectGenerator ?? throw new ArgumentNullException(nameof(objectGenerator));
            _objects = new ConcurrentBag<T>();
        }

        public T Get() => _objects.TryTake(out T item) ? item : _objectGenerator();

        public void Return(T item) => _objects.Add(item);
    }

    class Program
    {
        static void Main(string[] args)
        {
            using var cts = new CancellationTokenSource();

            // Create an opportunity for the user to cancel.
            _ = Task.Run(() =>
            {
                if (char.ToUpperInvariant(Console.ReadKey().KeyChar) == 'C')
                {
                    cts.Cancel();
                }
            });

            var pool = new ObjectPool<ExampleObject>(() => new ExampleObject());

            // Create a high demand for ExampleObject instance.
            Parallel.For(0, 1000000, (i, loopState) =>
            {
                var example = pool.Get();
                try
                {
                    Console.CursorLeft = 0;
                    // This is the bottleneck in our application. All threads in this loop
                    // must serialize their access to the static Console class.
                    Console.WriteLine($"{example.GetValue(i):####.####}");
                }
                finally
                {
                    pool.Return(example);
                }

                if (cts.Token.IsCancellationRequested)
                {
                    loopState.Stop();
                }
            });

            Console.WriteLine("Press the Enter key to exit.");
            Console.ReadLine();
        }
    }

    // A toy class that requires some resources to create.
    // You can experiment here to measure the performance of the
    // object pool vs. ordinary instantiation.
    class ExampleObject
    {
        public int[] Nums { get; set; }

        public ExampleObject()
        {
            Nums = new int[1000000];
            var rand = new Random();
            for (int i = 0; i < Nums.Length; i++)
            {
                Nums[i] = rand.Next();
            }
        }

        public double GetValue(long i) => Math.Sqrt(Nums[i]);
    }
}
Imports System.Collections.Concurrent
Imports System.Threading
Imports System.Threading.Tasks

Module ObjectPoolExample


    Public Class ObjectPool(Of T)
        Private _objects As ConcurrentBag(Of T)
        Private _objectGenerator As Func(Of T)

        Public Sub New(ByVal objectGenerator As Func(Of T))
            If objectGenerator Is Nothing Then Throw New ArgumentNullException("objectGenerator")
            _objects = New ConcurrentBag(Of T)()
            _objectGenerator = objectGenerator
        End Sub

        Public Function GetObject() As T
            Dim item As T
            If _objects.TryTake(item) Then Return item
            Return _objectGenerator()
        End Function

        Public Sub PutObject(ByVal item As T)
            _objects.Add(item)
        End Sub
    End Class


    Sub Main()

        Dim cts As CancellationTokenSource = New CancellationTokenSource()

        ' Create an opportunity for the user to cancel.
        Task.Run(Sub()
                     If Console.ReadKey().KeyChar = "c"c Or Console.ReadKey().KeyChar = "C"c Then
                         cts.Cancel()
                     End If
                 End Sub)
        Dim pool As ObjectPool(Of TestClass) = New ObjectPool(Of TestClass)(Function() New TestClass())

        ' Create a high demand for TestClass objects.
        Parallel.For(0, 1000000, Sub(i, loopState)

                                     Dim mc As TestClass = pool.GetObject()
                                     Console.CursorLeft = 0
                                     ' This is the bottleneck in our application. All threads in this loop
                                     ' must serialize their access to the static Console class.
                                     Console.WriteLine("{0:####.####}", mc.GetValue(i))

                                     pool.PutObject(mc)
                                     If cts.Token.IsCancellationRequested Then
                                         loopState.Stop()

                                     End If
                                 End Sub)

        Console.WriteLine("Press the Enter key to exit.")
        Console.ReadLine()
        cts.Dispose()
    End Sub

    ' A toy class that requires some resources to create.
    ' You can experiment here to measure the performance of the
    ' object pool vs. ordinary instantiation.
    Class TestClass

        Public Nums() As Integer

        Public Function GetValue(ByVal i As Long) As Double
            Return Math.Sqrt(Nums(i))
        End Function

        Public Sub New()

            Nums = New Integer(10000000) {}
            '   ReDim Nums(1000000)
            Dim rand As Random = New Random()
            For i As Integer = 0 To Nums.Length - 1
                Nums(i) = rand.Next()
            Next
        End Sub
    End Class

End Module

Vea tambiénSee also