- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章.NET Core对象池的应用:设计篇由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
《编程篇》已经涉及到了对象池模型的大部分核心接口和类型。对象池模型其实是很简单的,不过其中有一些为了提升性能而刻意为之的实现细节倒是值得我们关注。总的来说,对象池模型由三个核心对象构成,它们分别是表示对象池的ObjectPool<T>对象、对象值提供者的ObjectPoolProvider对象,已及控制池化对象创建与释放行为的IPooledObjectPolicy<T>对象,我们先来介绍最后一个对象.
。
我们在《编程篇》已经说过,表示池化对象策略的IPooledObjectPolicy<T>对象不仅仅帮助我们创建对象,还可以帮助我们执行一些对象回归对象池之前所需的回收操作,对象最终能否回到对象池中也受它的控制。如下面的代码片段所示,IPooledObjectPolicy<T>接口定义了两个方法,Create方法用来创建池化对象,对象回归前需要执行的操作体现在Return方法上,该方法的返回值决定了指定的对象是否应该回归对象池。抽象类PooledObjectPolicy<T>实现了该接口,我们一般将它作为自定义策略类型的基类.
public interface IPooledObjectPolicy<T>{ T Create(); bool Return(T obj);}public abstract class PooledObjectPolicy<T> : IPooledObjectPolicy<T>{ protected PooledObjectPolicy(){} public abstract T Create(); public abstract bool Return(T obj);}
我们默认使用的是如下这个DefaultPooledObjectPolicy<T>类型,由于它直接通过反射来创建池化对象,所以要求泛型参数T必须有一个公共的默认无参构造函数。它的Return方法直接返回True,意味着提供的对象可以被无限制地复用.
public class DefaultPooledObjectPolicy<T> : PooledObjectPolicy<T> where T: class, new(){ public override T Create() => Activator.CreateInstance<T>(); public override bool Return(T obj) => true;}
。
对象池通过ObjectPool<T>对象表示。如下面的代码片段所示,ObjectPool<T>是一个抽象类,池化对象通过Get方法提供给我们,我们在使用完之后调用Return方法将其释放到对象池中以供后续复用.
public abstract class ObjectPool<T> where T: class{ protected ObjectPool(){} public abstract T Get(); public abstract void Return(T obj);}
我们默认使用的对象池体现为一个DefaultObjectPool<T>对象,由于针对对象池的绝大部分实现就体现这个类型中,所以它也是本节重点讲述的内容。我们在前面一节已经说过,对象池具有固定的大小,并且默认的大小为处理器个数的2倍。我们假设对象池的大小为N,那么DefaultObjectPool<T>对象会如下图所示的方式使用一个单一对象和一个长度为N-1的数组来存放由它提供的N个对象.
如下面的代码片段所示,DefaultObjectPool<T>使用字段_firstItem用来存放第一个池化对象,余下的则存放在_items字段表示的数组中。值得注意的是,这个数组的元素类型并非池化对象的类型T,而是一个封装了池化对象的结构体ObjectWrapper。如果该数组元素类型改为引用类型T,那么当我们对某个元素进行复制的时候,运行时会进行类型校验(要求指定对象类型派生于T),无形之中带来了一定的性能损失(值类型数组就不需求进行派生类型的校验)。我们在前面提到过,对象池中存在一些性能优化的细节,这就是其中之一.
public class DefaultObjectPool<T> : ObjectPool<T> where T : class{ private protected T _firstItem; private protected readonly ObjectWrapper[] _items; … private protected struct ObjectWrapper { public T Element; }}
DefaultObjectPool<T>类型定义了如下两个构造函数。我们在创建一个DefaultObjectPool<T>对象的时候会提供一个IPooledObjectPolicy<T>对象并指定对象池的大小。对象池的大小默认设置为处理器数量的2倍体现在第一个构造函数重载中。如果指定的是一个DefaultPooledObjectPolicy<T>对象,表示默认池化对象策略的_isDefaultPolicy字段被设置成True。因为DefaultPooledObjectPolicy<T>对象的Return方法总是返回True,并且没有任何具体的操作,所以在将对象释放回对象池的时候就不需要调用Return方法了,这是第二个性能优化的细节.
public class DefaultObjectPool<T> : ObjectPool<T> where T : class{ private protected T _firstItem; private protected readonly ObjectWrapper[] _items; private protected readonly IPooledObjectPolicy<T> _policy; private protected readonly bool _isDefaultPolicy; private protected readonly PooledObjectPolicy<T> _fastPolicy; public DefaultObjectPool(IPooledObjectPolicy<T> policy) : this(policy, Environment.ProcessorCount * 2) {} public DefaultObjectPool(IPooledObjectPolicy<T> policy, int maximumRetained) { _policy = policy ; _fastPolicy = policy as PooledObjectPolicy<T>; _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();}
从第二个构造函数的定义可以看出,指定的IPooledObjectPolicy<T>对象除了会赋值给_policy字段之外,如果提供的是一个PooledObjectPolicy<T>对象,该对象还会同时赋值给另一个名为_fastPolicy的字段。在进行池化对象的提取和释放时,_fastPolicy字段表示的池化对象策略会优先选用,这个逻辑体现在Create方法上。因为调用类型的方法比调用接口方法具有更好的性能(所以该字段才会命名为_fastPolicy),这是第三个性能优化的细节。这个细节还告诉我们在自定义池化对象策略的时候,最好将PooledObjectPolicy<T>作为基类,而不是直接实现IPooledObjectPolicy<T>接口.
如下所示的是重写的Get和Return方法的定义。用于提供池化对象的Get方法很简单,它会采用原子操作使用Null将_firstItem字段表示的对象“替换”下来,如果该字段不为Null,那么将其作为返回的对象,反之它会遍历数组的每个ObjectWrapper对象,并使用Null将其封装的对象“替换”下来,第一个成功替换下来的对象将作为返回值。如果所有ObjectWrapper对象封装的对象都为Null,意味着所有对象都被“借出”或者尚未创建,此时返回创建的新对象了.
public class DefaultObjectPool<T> : ObjectPool<T> 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<T>对象的Return方法来判断。从代码片段可以看出,这里依然会优先选择_fastPolicy字段表示的PooledObjectPolicy<T>对象以获得更好的性能.
在确定指定的对象可以释放回对象之后,如果_firstItem字段为Null,Return方法会采用原子操作使用指定的对象将其“替换”下来。如果该字段不为Null或者原子替换失败,该方法会便利数组的每个ObjectWrapper对象,并采用原子操作将它们封装的空引用替换成指定的对象。整个方法会在某个原子替换操作成功或者整个便利过程结束之后返回.
DefaultObjectPool<T>之所有使用一个数组附加一个单一对象来存储池化对象,是因为针对单一字段的读写比针对数组元素的读写具有更好的性能。从上面给出的代码可以看出,不论是Get还是Return方法,优先选择的都是_firstItem字段。如果池化对象的使用率不高,基本上使用的都会是该字段存储的对象,那么此时的性能是最高的.
通过前面的示例演示我们知道,当池化对象类型实现了IDisposable接口的情况下,如果某个对象在回归对象池的时候,对象池已满,该对象将被丢弃。与此同时,被丢弃对象的Dispose方法将立即被调用。但是这种现象并没有在DefaultObjectPool<T>类型的代码中体现出来,这是为什么呢?实际上DefaultObjectPool<T>还有如下这个名为DisposableObjectPool<T>的派生类。如代码片段可以看出,表示池化对象类型的泛型参数T要求实现IDisposable接口。如果池化对象类型实现了IDisposable接口,通过默认ObjectPoolProvider对象创建的对象池就是一个DisposableObjectPool<T>对象.
internal sealed class DisposableObjectPool<T> : DefaultObjectPool<T>, IDisposable where T : class{ private volatile bool _isDisposed; public DisposableObjectPool(IPooledObjectPolicy<T> policy) : base(policy) {} public DisposableObjectPool(IPooledObjectPolicy<T> 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<T>自身类型也实现了IDisposable接口,它会在Dispose方法中调用目前对象池中的每个对象的Dispose方法。用于提供池化对象的Get方法除了会验证自身的Disposed状态之外,并没有特别之处。当对象未能成功回归对象池,通过调用该对象的Dispose方法将其释放的操作体现在重写的Return方法中.
。
表示对象池的ObjectPool<T>对象是通过ObjectPoolProvider提供的。如下面的代码片段所示,抽象类ObjectPoolProvider定义了两个重载的Create<T>方法,抽象方法需要指定具体的池化对象策略。另一个重载由于采用默认的池化对象策略,所以要求对象类型具有一个默认无参构造函数.
public abstract class ObjectPoolProvider{ public ObjectPool<T> Create<T>() where T : class, new() => Create<T>(new DefaultPooledObjectPolicy<T>()); public abstract ObjectPool<T> Create<T>(IPooledObjectPolicy<T> policy) where T : class;}
在前面的示例演示中,我们使用的是如下这个DefaultObjectPoolProvider类型。如代码片段所示,DefaultObjectPoolProvider派生于抽象类ObjectPoolProvider,在重写的Create<T>方法中,它会根据泛型参数T是否实现IDisposable接口分别创建DisposableObjectPool<T>和DefaultObjectPool<T>对象.
public class DefaultObjectPoolProvider : ObjectPoolProvider{ public int MaximumRetained { get; set; } = Environment.ProcessorCount * 2; public override ObjectPool<T> Create<T>(IPooledObjectPolicy<T> policy) => typeof(IDisposable).IsAssignableFrom(typeof(T)) ? new DisposableObjectPool<T>(policy, MaximumRetained) : new DefaultObjectPool<T>(policy, MaximumRetained);}
DefaultObjectPoolProvider类型定义了一个标识对象池大小的MaximumRetained属性,采用处理器数量的两倍作为默认容量也体现在这里。这个属性并非只读,所以我们可以利用它根据具体需求调整提供对象池的大小。在ASP.NET应用中,我们基本上都会采用依赖注入的方式利用注入的ObjectPoolProvider对象来创建针对具体类型的对象池。我们在《编程篇》还演示了另一种创建对象池的方式,那就是直接调用ObjectPool类型的静态Create<T>方法,该方法的实现体现在如下所示的代码片段中.
public static class ObjectPool{ public static ObjectPool<T> Create<T>(IPooledObjectPolicy<T> policy) where T: class, new() => new DefaultObjectPoolProvider().Create<T>(policy ?? new DefaultPooledObjectPolicy<T>());}
到目前为止,我们已经将整个对象池的设计模型进行了完整的介绍。总得来说,这是一个简单、高效并且具有可扩展性的对象池框架,该模型涉及的几个核心接口和类型体现在如下图所示的UML中.
.NET Core对象池的应用:编程篇 。
.NET Core对象池的应用:扩展篇 。
到此这篇关于.NET Core对象池的应用:设计篇的文章就介绍到这了,更多相关.NET Core对象池的应用内容请搜索我以前的文章或继续浏览下面的相关文章希望大家以后多多支持我! 。
原文链接:https://www.cnblogs.com/artech/p/object-pool-02.html 。
最后此篇关于.NET Core对象池的应用:设计篇的文章就讲到这里了,如果你想了解更多关于.NET Core对象池的应用:设计篇的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
最近,我们将专用 SQL 池部署到生产中的 Synapse 工作区。在开发中,我们可以访问无服务器 SQL 池和专用 SQL 池。但是,在生产中,我们可以访问无服务器 SQL 池,但无法访问专用 SQ
假设您从一个项目公开 WCF 服务,并使用“添加服务引用”(在本例中为 Framework 3.5 WPF 应用程序)在另一个项目中使用它。 当您重新实例化 ClientBase 派生代理时,Clie
我有一个函数,它使用 multiprocessing.Pool 并行处理一个数据集中的所有数据。 from multiprocessing import Pool ... def func():
我正在尝试使用进程对象在 python 中使用工作池。每个 worker (一个进程)进行一些初始化(花费大量时间),传递一系列作业(理想情况下使用 map()),并返回一些东西。除此之外,不需要任何
我是软件工程师,最近我构建了我的 Linux 机器,想探索更多系统管理员类型的任务。我已经探索并阅读了很多关于 ZFS 的内容,但我越来越困惑,因为每篇文章对它的描述都不一样。 Everything
我有 zfs 池: $ sudo zpool status lxd pool: lxd state: ONLINE scan: none requested config: NAME
我有一个基于 Actor 的项目,对于其中的一部分,我必须使用一些接收消息的 Actor ,然后一个 Actor 分别分配给每个请求,每个 Actor 负责执行其消息请求,所以我需要类似线程的东西我的
我已经使用 QEMU 模拟器成功地将 FreeBSD 安装到原始图像文件中。我已经使用 ZFS 文件系统 (ZFS POOL) 格式化了图像文件。 使用下面的命令我已经成功地挂载了准备好由 zpool
我正在使用 multiprocessor.Pool并行处理一些文件。该代码等待接收文件,然后使用 Pool.apply_async 将该文件发送给工作人员。 ,然后处理文件。 这段代码应该一直在运行,
我正在使用带有光滑的 Bonecp 数据源。并发现池包含关闭的连接所以我总是遇到这个异常 java.sql.SQLException: Connection is closed! at com
我有apartment gem的 Multi-Tenancy Rails应用程序,我可以使用apartment-sidekiq在每个工作程序中成功切换数据库租户。但是,sidekiq worker 正
ZFS 池可能由数据集(文件系统、快照等)或卷组成。 ZFS 卷就像 block 设备,但我不明白池和文件系统之间的区别。当我通过 zpool create pool1 sda sdb sdc 创建
我在 docker 容器上运行了 airflow。我正在使用 airflow 2.0.2 版。 我知道我实际上可以通过 UI 创建池。但我正在寻找一种通过 pools.json 文件在 docker
我在tomcat中有一个jdbc池,用于建立数据库连接。我在使用后没有显式关闭连接对象。我的“maxActive”参数设置为100。应用程序运行了一段时间,但随后失败进行数据库查询。它会等待无限时间来
阅读 PostgreSQL 文档 here我读了以下内容: As well, connections requested for users other than the default config
我在 docker 容器上运行了 airflow。我正在使用 airflow 2.0.2 版。 我知道我实际上可以通过 UI 创建池。但我正在寻找一种通过 pools.json 文件在 docker
我正在读取一个大的 URL 文件并向服务发出请求。该请求由返回 ListenableFuture 的客户端执行。现在我想保留一个 ListenableFuture 池,例如最多同时执行 N 个 Fut
我想使用队列来保存结果,因为我希望消费者(串行而不是并行)在工作人员产生结果时处理工作人员的结果。 现在,我想知道为什么以下程序挂起。 import multiprocessing as mp imp
我正在开发一个单页应用程序,目前正在构建一个 JQuery、ajax 函数,以便我的所有调用都能通过。 对于一个典型的页面,我可能有 3 个 ajax 调用。我的想法是,如果用户互联网出去将这些 aj
我有一个单位类及其一些子类(弓箭手、剑客等)。我怎样才能创建一个回收所有单元类型子类的池? 最佳答案 这是不可能的,因为池只能包含一种特定类型的对象。否则你可能会遇到这样的情况: Pool unitP
我是一名优秀的程序员,十分优秀!