Use of object pool Object Pool in NET Core

  • 2021-12-04 09:54:53
  • OfStack

Directory 1. What is Object Pool 2. Object Pool in NET Core 3. Summary of this article

1. What is an object pool

Object pool is simply a software design idea that provides reusable ability for objects. We often say that it is not difficult to borrow and return, and object pool ensures that objects can be reused through two actions: borrowing and returning, thus saving the performance overhead of frequently creating objects. The most common scenario of object pool is game design, because there are a large number of reusable objects in the game, and a steady stream of bullets is not recycled. There is something called connection pool in the database. When the database cannot be connected, experienced developers often check whether the connection pool is full first, which is actually the concrete implementation of object pool pattern in specific fields. Therefore, the object pool is essentially a container responsible for the creation and destruction of a set of objects. The greatest advantage of an object pool is that it can manage each object in the pool autonomously and decide whether they need to be recycled or reused. We all know that creating a new object consumes a certain amount of system resources, and once these objects can be used repeatedly, the system resource overhead can be saved, which will be very helpful to improve system performance. The following code is a simple object pool implemented by Microsoft official documents:



public class ObjectPool<T> : IObjectPool<T>

{

	private Func<T> _instanceFactory;

	private ConcurrentBag<T> _instanceItems;

	public ObjectPool(Func<T> instanceFactory)

	{

		_instanceFactory = instanceFactory ?? 

		throw new ArgumentNullException(nameof(instanceFactory));

		_instanceItems = new ConcurrentBag<T>();

	}

	public T Get()

	{

		T item;

		if (_instanceItems.TryTake(out item)) return item;

		return _instanceFactory();

	}

	public void Return(T item)

	{

		_instanceItems.Add(item);

	}

}

2.. Object pools in NET Core

In .NET Core Microsoft has provided us with the implementation of object pool, namely Microsoft.Extensions.ObjectPool . It mainly provides three core components, which are ObjectPool , ObjectPoolProvider And IPooledObjectPolicy . ObjectPool Is an abstract class, externally provides Get and Return two methods, this is the so-called borrow and return. ObjectPoolProvider Is also an abstract class whose job is to create ObjectPool, and it provides two Create methods, the difference between which is that the parameterless version essentially uses DefaultPooledObjectPolicy . It and DefaultObjectPool DefaultObjectPoolProvider is the default implementation provided by Microsoft. IPooledObjectPolicy can define different policies for different object pools to decide how to borrow objects and whether they can be returned. DefaultObjectPool internally uses ObjectWrapper [] to manage objects, ObjectWrapper [] is equal in size to maximumRetained-1, and maximumRetained is equal by default to maximumRetained Environment.ProcessorCount * 2 , which is mainly used here Microsoft.Extensions.ObjectPool0 Method,

The specific code is as follows:


public override T Get()

{

  var item = _firstItem;

  if (item == null || Interlocked.CompareExchange(ref _firstItem, null, item) != item)

  {

    var items = _items;

    for (var i = 0; i < items.Length; i++)

    {

      item = items[i].Element;

      if (item != null && Interlocked.CompareExchange(ref items[i].Element, null, item) == item)

      {

        return item;

      }

    }

    item = Create();

  }

  return item;

}

// Non-inline to improve its code quality as uncommon path

[MethodImpl(MethodImplOptions.NoInlining)]

private T Create() => _fastPolicy?.Create() ?? _policy.Create();



public override void Return(T obj)

{

  if (_isDefaultPolicy || (_fastPolicy?.Return(obj) ?? _policy.Return(obj)))

  {

    if (_firstItem != null || Interlocked.CompareExchange(ref _firstItem, obj, null) != null)

    {

      var items = _items;

      for (var i = 0; i < items.Length && Interlocked.CompareExchange(ref items[i].Element, obj, null) != null; ++i)

      {

      }

    }

  }

}

Used here Microsoft.Extensions.ObjectPool0 Method, the Get () method sets the items[i].Element And null Exchange, set the specified element to null and return the original value. The Return () method sets the items[i].Element The value swapped with obj is not null, indicating that the specified element has been returned. This method swaps only if the first parameter and the third parameter are equal.

Having said that, let's look at the specific usage of object pool under 1:


var service = new ServiceCollection();

// Use DefaultObjectPoolProvider

service.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();

// Use the default policy 

service.AddSingleton<ObjectPool<Foo>>(serviceProvider =>

{

  var objectPoolProvider = serviceProvider.GetRequiredService<ObjectPoolProvider>();

  return objectPoolProvider.Create<Foo>();

});

// Using custom policies 

service.AddSingleton<ObjectPool<Foo>>(serviceProvider =>

{

  var objectPoolProvider = serviceProvider.GetRequiredService<ObjectPoolProvider>();

  return objectPoolProvider.Create(new FooObjectPoolPolicy());

});



var serviceProvider = _service.BuildServiceProvider();



var objectPool = _serviceProvider.GetService<ObjectPool<Foo>>();



// Borrowing and paying back, the two times are the same 1 Objects 

var item1 = objectPool.Get();

objectPool.Return(item1);

var item2 = objectPool.Get();

Assert.AreEqual(item1, item2);//true



// Borrowing or not paying back, two are different objects 

var item3 = objectPool.Get();

var item4 = objectPool.Get();

Assert.AreEqual(item3, item4);//false

In the above code, Foo and FooObjectPoolPolicy are two tool classes:


public class Foo

{

  public string Id { get; set; }

  public DateTime? CreatedAt { get; set; }

  public string CreatedBy { get; set; }

}



public class FooObjectPoolPolicy : IPooledObjectPolicy<Foo>

{

  public Foo Create()

  {

    return new Foo()

    {

      Id = Guid.NewGuid().ToString("N"),

      CreatedAt = DateTime.Now,

      CreatedBy = "zs"

    };

  }



  public bool Return(Foo obj)

  {

    return true;

  }

}

TIP: When you need to control how objects in the object pool are created, you can consider implementing custom IPooledObjectPolicy<T> On the contrary DefaultPooledObjectPolicy<T> The implementation can completely satisfy your use.

3. Summary of this article

Implementing object pooling can consider ConcurrentBag , Stack, Queue As well as BlockingCollection And Microsoft has implemented a simple object pool for us in. NET Core. In most cases, we only need to define our own IPooledObjectPolicy To decide how to borrow and return the object. In short, in the game world, ObjectPool1 Connection pool in database is the concrete implementation of object pool pattern in their respective fields.

TIP: Object pool is a software design pattern that reduces resource overhead and improves system performance by reusing objects. Its core is to control the life cycle of objects in the container to avoid active recovery of the system. Objects borrowed from the object pool must be returned in time, otherwise there will be no available resources in the object pool.


Related articles: