Lezen in het Engels

Delen via


Een objectgroep maken met behulp van een ConcurrentBag

In dit voorbeeld ziet u hoe u een objectgroep implementeert met een ConcurrentBag<T> objectgroep. Objectgroepen kunnen de prestaties van toepassingen verbeteren in situaties waarin u meerdere exemplaren van een klasse nodig hebt en de klasse duur is om te maken of te vernietigen. Wanneer een clientprogramma een nieuw object aanvraagt, probeert de objectgroep eerst een object op te geven dat al is gemaakt en geretourneerd aan de pool. Als er geen beschikbaar is, wordt er alleen een nieuw object gemaakt.

Het ConcurrentBag<T> wordt gebruikt om de objecten op te slaan omdat het ondersteuning biedt voor snelle invoeging en verwijdering, vooral wanneer dezelfde thread zowel items toevoegt als verwijdert. Dit voorbeeld kan verder worden uitgebreid om te worden gebouwd rond een IProducerConsumerCollection<T>, die door de gegevensstructuur van de zak wordt geïmplementeerd, zoals wel ConcurrentQueue<T> en ConcurrentStack<T>.

Tip

In dit artikel wordt gedefinieerd hoe u uw eigen implementatie van een objectgroep schrijft met een onderliggend gelijktijdig type om objecten op te slaan voor hergebruik. Het Microsoft.Extensions.ObjectPool.ObjectPool<T> type bestaat echter al onder de Microsoft.Extensions.ObjectPool naamruimte. Overweeg het beschikbare type te gebruiken voordat u uw eigen implementatie maakt, met veel extra functies.

Voorbeeld

C#
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]);
    }
}

Zie ook