In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article mainly introduces the example analysis of. NET Core object pool, which has certain reference value. Interested friends can refer to it. I hope you will gain a lot after reading this article. Let Xiaobian take you to understand it together.
IPooledObjectPolicy
The IPooledObjectPolicy object of the pooled object policy not only helps us create objects, but also helps us perform some recycling operations before objects return to the object pool. It also controls whether objects eventually return to the object pool. As shown in the code snippet below, the IPooledObjectPolicy interface defines two methods, the Create method to create pooled objects, and the Return method to perform operations before returning objects, the return value of which determines whether the specified object should return to the object pool. The abstract class PooledObjectPolicy implements this interface, and we generally use it as a base class for custom policy types.
public interface IPooledObjectPolicy{ T Create(); bool Return(T obj);}public abstract class PooledObjectPolicy : IPooledObjectPolicy{ protected PooledObjectPolicy(){} public abstract T Create(); public abstract bool Return(T obj);}
We default to the following DefaultPooledObjectPolicy type, which creates pooled objects directly through reflection, requiring generic parameter T to have a common default parameterless constructor. Its Return method returns True directly, meaning that the supplied object can be reused without restriction.
public class DefaultPooledObjectPolicy : PooledObjectPolicy where T: class, new(){ public override T Create() => Activator.CreateInstance(); public override bool Return(T obj) => true;} II. ObjectPool
Object pools are represented by ObjectPool objects. As shown in the code snippet below, ObjectPool is an abstract class, the pooled object is provided to us through the Get method, and we call the Return method to release it into the object pool for later reuse.
public abstract class ObjectPool where T: class{ protected ObjectPool(){} public abstract T Get(); public abstract void Return(T obj);}DefaultObjectPool
The object pool we use by default is embodied as a DefaultObjectPool object, and since most implementations for object pools are embodied in this type, it is also the focus of this section. As we said in the previous section, the object pool has a fixed size, and the default size is twice the number of processors. Assuming the size of the object pool is N, the DefaultObjectPool object uses a single object and an array of length N-1 to store the N objects it provides, as shown below.
As shown in the code snippet below, DefaultObjectPool uses the field_firstItem to store the first pooled object, and the rest are stored in the array represented by the_items field. It is worth noting that the element type of this array is not the type T of the pooled object, but a struct ObjectWrapper that encapsulates the pooled object. If the element type of the array is changed to reference type T, then when we copy an element, type checking will be performed at runtime (the specified object type is required to derive from T), which will inevitably bring some performance loss (the value type array does not need to perform derived type checking). As we mentioned earlier, there are some performance optimization details in object pools, and this is one of them.
public class DefaultObjectPool : ObjectPool where T : class{ private protected T _firstItem; private protected readonly ObjectWrapper[] _items; … private protected struct ObjectWrapper { public T Element; }}
The DefaultObjectPool type defines two constructors: When we create a DefaultObjectPool object, we provide an IPooledObjectPolicy object and specify the size of the object pool. The size of the object pool is set by default to twice the number of processors reflected in the first constructor overload. If a DefaultPooledObjectPolicy object is specified, the_isDefaultPolicy field indicating the default pooled object policy is set to True. Because the Return method of a DefaultPooledObjectPolicy object always returns True and there is no specific action, there is no need to call the Return method when releasing the object back into the object pool, which is the second performance optimization detail.
public class DefaultObjectPool : ObjectPool where T : class{ private protected T _firstItem; private protected readonly ObjectWrapper[] _items; private protected readonly IPooledObjectPolicy _policy; private protected readonly bool _isDefaultPolicy; private protected readonly PooledObjectPolicy _fastPolicy; public DefaultObjectPool(IPooledObjectPolicy policy) : this(policy, Environment.ProcessorCount * 2) {} public DefaultObjectPool(IPooledObjectPolicy policy, int maximumRetained) { _policy = policy ; _fastPolicy = policy as PooledObjectPolicy; _isDefaultPolicy = IsDefaultPolicy(); _items = new ObjectWrapper[maximumRetained - 1]; bool IsDefaultPolicy() { var type = policy.GetType(); return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(DefaultPooledObjectPolicy); } } [MethodImpl(MethodImplOptions.NoInlining)] private T Create() => _fastPolicy?. Create() ?? _policy.Create();}
As can be seen from the definition of the second constructor, the specified IPooledObjectPolicy object will be assigned to the_policy field, and if a PooledObjectPolicy object is provided, the object will also be assigned to another field named_fastPolicy. When extracting and releasing pooled objects, the pooled object policy represented by the_fastPolicy field will be preferred. This logic is reflected in the Create method. This field is named_fastPolicy because methods of the calling type have better performance than methods of the calling interface. This is the third performance optimization detail. This detail also tells us that when customizing pooled object policies, it is better to use PooledObjectPolicy as a base class rather than implementing the IPooledObjectPolicy interface directly.
Shown below are the definitions of the overridden Get and Return methods. The Get method used to provide pooled objects is simple. It uses an atomic operation to "replace" the object represented by the_firstItem field with null. If the field is not Null, it will be the returned object. Otherwise, it will traverse each ObjectWrapper object of the array and "replace" the object it encapsulates with Null. The first successfully replaced object will be the returned value. If all the objects encapsulated by the ObjectWrapper object are Null, it means that all objects are "loaned" or not yet created, and the new objects created are returned.
public class DefaultObjectPool : ObjectPool where T : class{ 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; } 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) {} } } } …} 将对象释放会对象池的Return方法也很好理解。首先它需要判断指定的对象能否释放会对象池中,如果使用的是默认的池化对象策略,答案是肯定的,否则只能通过调用IPooledObjectPolicy对象的Return方法来判断。从代码片段可以看出,这里依然会优先选择_fastPolicy字段表示的PooledObjectPolicy对象以获得更好的性能。 在确定指定的对象可以释放回对象之后,如果_firstItem字段为Null,Return方法会采用原子操作使用指定的对象将其"替换"下来。如果该字段不为Null或者原子替换失败,该方法会便利数组的每个ObjectWrapper对象,并采用原子操作将它们封装的空引用替换成指定的对象。整个方法会在某个原子替换操作成功或者整个便利过程结束之后返回。 DefaultObjectPool之所有使用一个数组附加一个单一对象来存储池化对象,是因为针对单一字段的读写比针对数组元素的读写具有更好的性能。从上面给出的代码可以看出,不论是Get还是Return方法,优先选择的都是_firstItem字段。如果池化对象的使用率不高,基本上使用的都会是该字段存储的对象,那么此时的性能是最高的。 DisposableObjectPool 通过前面的示例演示我们知道,当池化对象类型实现了IDisposable接口的情况下,如果某个对象在回归对象池的时候,对象池已满,该对象将被丢弃。与此同时,被丢弃对象的Dispose方法将立即被调用。但是这种现象并没有在DefaultObjectPool类型的代码中体现出来,这是为什么呢?实际上DefaultObjectPool还有如下这个名为DisposableObjectPool的派生类。如代码片段可以看出,表示池化对象类型的泛型参数T要求实现IDisposable接口。如果池化对象类型实现了IDisposable接口,通过默认ObjectPoolProvider对象创建的对象池就是一个DisposableObjectPool对象。 internal sealed class DisposableObjectPool : DefaultObjectPool, IDisposable where T : class{ private volatile bool _isDisposed; public DisposableObjectPool(IPooledObjectPolicy policy) : base(policy) {} public DisposableObjectPool(IPooledObjectPolicy policy, int maximumRetained) : base(policy, maximumRetained) {} public override T Get() { if (_isDisposed) { throw new ObjectDisposedException(GetType().Name); } return base.Get(); } public override void Return(T obj) { if (_isDisposed || !ReturnCore(obj)) { DisposeItem(obj); } } private bool ReturnCore(T obj) { bool returnedToPool = false; if (_isDefaultPolicy || (_fastPolicy?.Return(obj) ?? _policy.Return(obj))) { if (_firstItem == null && Interlocked.CompareExchange(ref _firstItem, obj, null) == null) { returnedToPool = true; } else { var items = _items; for (var i = 0; i < items.Length && !(returnedTooPool = Interlocked.CompareExchange(ref items[i].Element, obj, null) == null); i++) {} } } return returnedTooPool; } public void Dispose() { _isDisposed = true; DisposeItem(_firstItem); _firstItem = null; ObjectWrapper[] items = _items; for (var i = 0; i < items.Length; i++) { DisposeItem(items[i].Element); items[i].Element = null; } } private void DisposeItem(T item) { if (item is IDisposable disposable) { disposable.Dispose(); } }} 从上面代码片段可以看出,DisposableObjectPool自身类型也实现了IDisposable接口,它会在Dispose方法中调用目前对象池中的每个对象的Dispose方法。用于提供池化对象的Get方法除了会验证自身的Disposed状态之外,并没有特别之处。当对象未能成功回归对象池,通过调用该对象的Dispose方法将其释放的操作体现在重写的Return方法中。 三、ObjectPoolProvider 表示对象池的ObjectPool对象是通过ObjectPoolProvider提供的。如下面的代码片段所示,抽象类ObjectPoolProvider定义了两个重载的Create方法,抽象方法需要指定具体的池化对象策略。另一个重载由于采用默认的池化对象策略,所以要求对象类型具有一个默认无参构造函数。 public abstract class ObjectPoolProvider{ public ObjectPool Create() where T : class, new() =>Create(new DefaultPooledObjectPolicy()); public abstract ObjectPool Create(IPooledObjectPolicy policy) where T : class;}
In the previous example demonstration, we used the following DefaultObjectPoolProvider type. As the code snippet shows, DefaultObjectPoolProvider is derived from the abstract class ObjectPoolProvider, and in the overridden Create method it creates DisposableObjectPool and DefaultObjectPool objects, respectively, depending on whether the generic parameter T implements the IDisposable interface.
public class DefaultObjectPoolProvider : ObjectPoolProvider{ public int MaximumRetained { get; set; } = Environment.ProcessorCount * 2; public override ObjectPool Create(IPooledObjectPolicy policy) => typeof(IDisposable).IsAssignableFrom(typeof(T)) ? new DisposableObjectPool(policy, MaximumRetained) : new DefaultObjectPool(policy, MaximumRetained);}
The DefaultObjectPoolProvider type defines a MaximumRetained property that identifies the size of the object pool, as well as the default capacity of twice the number of processors. This property is not read-only, so we can use it to resize the pool of objects provided to us according to our specific needs. In ASP.NET applications, we basically use dependency injection to create a pool of objects for specific types using injected ObjectPoolProvider objects. Another way to create an object pool, as we demonstrated in Programming, is to call directly the static Create method of the ObjectPool type, which is implemented in the code snippet shown below.
public static class ObjectPool{ public static ObjectPool Create(IPooledObjectPolicy policy) where T: class, new() => new DefaultObjectPoolProvider().Create(policy ?? new DefaultPooledObjectPolicy());}
So far, we have covered the entire object pool design model in its entirety. Overall, this is a simple, efficient, and extensible object pooling framework, with several core interfaces and types represented in UML as shown below.
Thank you for reading this article carefully. I hope that the article "Sample Analysis of. NET Core Object Pool" shared by Xiaobian will be helpful to everyone. At the same time, I hope that everyone will support it a lot and pay attention to the industry information channel. More relevant knowledge is waiting for you to learn!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.