Use of object pool Object Pool in NET Core
- 2021-12-04 09:54:53
- OfStack
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.ObjectPool
0
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.ObjectPool
0
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,
ObjectPool
1
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.