Cómo: Crear un grupo de objetos usando ConcurrentBagHow to: Create an Object Pool by Using a ConcurrentBag

Este ejemplo muestra cómo usar un contenedor simultáneo para implementar un grupo de objetos.This example shows how to use a concurrent bag 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.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>.

EjemploExample

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


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

        public ObjectPool(Func<T> objectGenerator)
        {
            if (objectGenerator == null) throw new ArgumentNullException("objectGenerator");
            _objects = new ConcurrentBag<T>();
            _objectGenerator = objectGenerator;
        }

        public T GetObject()
        {
            T item;
            if (_objects.TryTake(out item)) return item;
            return _objectGenerator();
        }

        public void PutObject(T item)
        {
            _objects.Add(item);
        }
    }

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

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

            ObjectPool<MyClass> pool = new ObjectPool<MyClass> (() => new MyClass());            

            // Create a high demand for MyClass objects.
            Parallel.For(0, 1000000, (i, loopState) =>
                {
                    MyClass mc = 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)
                        loopState.Stop();                 
 
                });
            Console.WriteLine("Press the Enter key to exit.");
            Console.ReadLine();
            cts.Dispose();
        }

    }

    // 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 MyClass
    {
        public int[] Nums {get; set;}
        public double GetValue(long i)
        {
            return Math.Sqrt(Nums[i]);
        }
        public MyClass()
        {
            Nums = new int[1000000];
            Random rand = new Random();
            for (int i = 0; i < Nums.Length; i++)
                Nums[i] = rand.Next();
        }
    }   
}
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