.NET Core 3. aec 0之深入源码理解ObjectPool(一)
写在前面
对象池是一种比较常用的提高系统性能的软件设计模式,它维护了一系列相关对象列表的容器对象,这些对象可以随时重复使用,对象池节省了频繁创建对象的开销。
它使用取用/归还-重复取用的操作模式,如下图所示:
本文将主要介绍对象池的基本概念、对象池的优势及其工作机制,下一篇文档将从源码角度介绍.NETCore3.0是如何实现对象池的。
对象池基础
对象池的基本概念
对象池的核心概念是容器,其表示形式可以认为是列表。每当有新的对象创建请求进入时,都会通过从池中分配一个对象来满足该请求。当我们需要获取某个对象时,可以从池中获取。既然有了对象池,那么也就很方便我们就很容易建立起对象的管理与追踪了了。
对象池的优势
我们知道一旦应用程序启动并运行,内存使用就会受到系统所需对象的数量和大小的影响。
我们知道创建一个对象的实例,是需要消耗一定的系统资源,尤其是该对象的构造十分复杂的时候,再加上需要频繁创建的时候,其实例化所消耗的资源更加昂贵。如果我们能有一种办法减少这种昂贵的系统开销,这对系统性能的提升是十分有帮助的。
对象池理念的出现,有助于我们解决复杂对象的重复创建所引发的资源开销问题。对象存储在某种类型的列表或者说数组中,我们可以和获取数组中的子项一样获取已经存在在对象池中的对象。
对象池的最大优点是,它可以自主管理内部已经创建的对象,包括回收和重复使用对象。程序在使用完某个对象后,会将其发还至对象池,而不是在内存中销毁他们。
对象池通过资源的分配,因而也就减少了应用程序所需的垃圾回收数量。这对于需要频繁创建同一对象的功能来说,对象池最大程度地减少了系统资源的消耗。
简单来说,对象池的设计目标就是要使对象可以得到重复使用,而不是被垃圾回收器回收。
对象池的工作机制
当客户端程序需要某个对象时,对象池首先尝试提供一个已经创建的对象。如果没有可用的对象,则会创建一个新对象。这类似于一个GetOrAdd的操作。同时对象池中对象的数量就会减少,直到该对象已经使用完,那么它就会被放回到对象池池中以等待使用。这就是为什么对象池有助于重用性、并减少了在获取对象时创建对象所涉及的开销的原因。
另外,需要注意的是,只要池中至少有一个对象,该池就会一直保留在内存中。只要对象池还在,里面的对象也会一直存在。
当对象池用于并发操作时,需要确保对象池是线程安全的,而且其本身还要有很高的性能。
ConcurrentBag对象池解决方案
这个解决方案来自于MSDN,
usingSystem;[code]usingSystem.Collections.Concurrent;
usingSystem.Threading;
usingSystem.Threading.Tasks;
namespaceObjectPoolExample
{
publicclassObjectPool<T>
{
privateConcurrentBag<T>_objects;
privateFunc<T>_objectGenerator;
publicObjectPool(Func<T>objectGenerator)
{
if(objectGenerator==null)thrownewArgumentNullException("objectGenerator");
_objects=newConcurrentBag<T>();
_objectGenerator=objectGenerator;
}
publicTGetObject()
{
Titem;
if(_objects.TryTake(outitem))returnitem;
return_objectGenerator();
}
publicvoidPutObject(Titem)
{
_objects.Add(item);
}
}
classProgram
{
staticvoidMain(string[]args)20bc
{
CancellationTokenSourcects=newCancellationTokenSource();
//Createanopportunityfortheusertocancel.
Task.Run(()=>
{
if(Console.ReadKey().KeyChar=='c'||Console.ReadKey().KeyChar=='C')
cts.Cancel();
});
ObjectPool<MyClass>pool=newObjectPool<MyClass>(()=>newMyClass());
//CreateahighdemandforMyClassobjects.
Parallel.For(0,1000000,(i,loopState)=>
{
MyClassmc=pool.GetObject();
Console.CursorLeft=0;
//Thisisthebottleneckinourapplication.Allthreadsinthisloop
//mustserializetheiraccesstothestaticConsoleclass.
Console.WriteLine("{0:####.####}",mc.GetValue(i));
pool.PutObject(mc);
if(cts.Token.IsCancellationRequested)
loopState.Stop();
});
Console.WriteLine("PresstheEnterkeytoexit.");
Console.ReadLine();
cts.Dispose();
}
}
//Atoyclassthatrequiressomeresourcestocreate.
//Youcanexperimentheretomeasuretheperformanceofthe
//objectpoolvs.ordinaryinstantiation.
classMyClass
{
publicint[]Nums{get;set;}
publicdoubleGetValue(longi)
{
returnMath.Sqrt(Nums[i]);
}
publicMyClass()
{
Nums=newint[1000000];
Randomrand=newRandom();
for(inti=0;i<Nums.Length;i++)
Nums[i]=rand.Next();
}
}
}[/code]
参考链接:https://docs.microsoft.com/en-us/dotnet/standard/collections/thread-safe/how-to-create-an-object-pool
- 【春华秋实】深入源码理解.NET Core中Startup的注册及运行
- .NET Core 3.0之深入源码理解Configuration(一)
- .NET Core 3.0之深入源码理解Configuration(三)
- .NET Core 3.0之深入源码理解Kestrel的集成与应用(一)
- .NET Core 3.0之深入源码理解HttpClientFactory(一)
- .NET Core 3.0之深入源码理解Kestrel的集成与应用(二)
- .NET Core 3.0之深入源码理解Configuration(二)
- 深入理解Tomcat系列之二:源码调试环境搭建
- 深入理解OkHttp源码(一)——提交请求
- tomcat4源码-深入理解tomcat
- java源码阅读之深入理解ThreadLocal
- 深入理解PHP之源码目录结构与功能说明
- 深入理解Spring 之 源码剖析AOP(注解方式)
- 深入理解ceph-disk prepare 源码逻辑
- 深入理解ButterKnife源码并掌握原理(四)
- 深入理解Feign之源码解析
- 从源码角度深入理解Toast
- 深入理解react-router@4.0 使用和源码解析
- Java源码解析:深入理解==和equals()
- 深入理解Zuul之源码解析