<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          .NET對象池的使用

          共 5174字,需瀏覽 11分鐘

           ·

          2021-07-05 18:25


          昨天在『.NET 大牛之路』技術(shù)群和大家聊到了對象池的話題,今天展開詳細(xì)講講這個知識點。

          這個概念大家都很熟悉,比如我們經(jīng)常聽到數(shù)據(jù)庫連接池和線程池。它是一種基于使用預(yù)先分配資源集合的性能優(yōu)化思想。

          簡單說,對象池就是對象的容器,旨在優(yōu)化資源的使用,通過在一個容器中池化對象,并根據(jù)需要重復(fù)使用這些池化對象來滿足性能上的需求。當(dāng)一個對象被激活時,便被從池中取出。當(dāng)對象被停用時,它又被放回池中,等待下一個請求。對象池一般用于對象的初始化過程代價較大或使用頻率較高的場景。

          那在 .NET 中如何實現(xiàn)或使用對象池呢?

          在 ASP.NET Core 框架里已經(jīng)內(nèi)置了一個對象池功能的實現(xiàn):Microsoft.Extensions.ObjectPool。如果是控制臺應(yīng)用程序,可以單獨安裝這個擴展庫。

          1池化策略

          首先,要使用 ObjectPool,需要創(chuàng)建一個池化策略,告訴對象池你將如何創(chuàng)建對象,以及如何歸還對象。

          該策略通過實現(xiàn)接口 IPooledObjectPolicy 來定義,下面是一個最簡單的策略實現(xiàn):

          public class FooPooledObjectPolicy : IPooledObjectPolicy<Foo>
          {
          public Foo Create()
          {
          return new Foo();
          }

          public bool Return(Foo obj)
          {
          return true;
          }
          }

          如果每次編碼都要定義這樣的策略,會比較麻煩,可以自己定義一個通用的泛型實現(xiàn)。Microsoft.Extensions.ObjectPool 中也提供了一個默認(rèn)的泛型實現(xiàn):DefaultPooledObjectPolicy<T>。如果不需要定義復(fù)雜的構(gòu)造邏輯,使用默認(rèn)的就行。下面我們來看看怎么使用。

          2對象池的使用

          對象池使用的原則是:有借有還,再借不難。

          當(dāng)對象池中沒有實例時,則創(chuàng)建實例并返回給調(diào)用組件;當(dāng)對象池中已有實例時,則直接取一個現(xiàn)有實例返回給調(diào)用組件。而且這個過程是線程安全的。

          Microsoft.Extensions.ObjectPool 提供了默認(rèn)的對象池實現(xiàn):DefaultObjectPool<T>,它提供了借 Get 和還 Return 操作接口。創(chuàng)建對象池時需要提供池化策略 IPooledObjectPolicy<T> 作為其構(gòu)造參數(shù)。

          var policy = new DefaultPooledObjectPolicy<Foo>();
          var pool = new DefaultObjectPool<Foo>(policy);

          我們來看一個常規(guī)示例(C# 9.0 單文件完整代碼):

          using Microsoft.Extensions.ObjectPool;
          using System;

          var policy = new DefaultPooledObjectPolicy<Foo>();
          var pool = new DefaultObjectPool<Foo>(policy);

          // 借
          var item1 = pool.Get();
          // 還
          pool.Return(item1);
          Console.WriteLine("item 1: {0}", item1.Id);

          // 借
          var item2 = pool.Get();
          // 還
          pool.Return(item2);
          Console.WriteLine("item 2: {0}", item2.Id);

          Console.ReadKey();

          public class Foo
          {
          public string Id { get; set; } = Guid.NewGuid().ToString("N");
          }

          打印結(jié)果:

          通過打印的 Id 知道,item1 和 item2 是同一樣對象。我們再來試試只借不還會是什么樣子。

          可以看到,兩個對象是不同的實例。所以,當(dāng)調(diào)用組件從對象池中借走一個對象實例,使用完后應(yīng)立即歸還給對象池,以便重復(fù)使用,避免因構(gòu)造新對象消耗過多資源。

          3指定對象池容量

          在創(chuàng)建 DefaultObjectPool<T> 時,還可以指定第二個參數(shù):對象池的容量。它表示最大可從該對象池取出的對象數(shù)量,指定數(shù)量以外的被取走的對象將不會被池化。我來演示一下,大家就知道什么意思了,請看示例:

          using Microsoft.Extensions.ObjectPool;
          using System;

          var policy = new DefaultPooledObjectPolicy<Foo>();

          // 指定容量為 2。
          var pool = new DefaultObjectPool<Foo>(policy, 2);

          // 借走 3 個
          var item1 = pool.Get();
          Console.WriteLine("item 1: {0}", item1.Id);
          var item2 = pool.Get();
          Console.WriteLine("item 2: {0}", item2.Id);
          var item3 = pool.Get();
          Console.WriteLine("item 3: {0}", item3.Id);

          // 再還會 3 個
          pool.Return(item1);
          pool.Return(item2);
          pool.Return(item3);


          // 再借走 3 個
          var item4 = pool.Get();
          Console.WriteLine("item 4: {0}", item4.Id);
          var item5 = pool.Get();
          Console.WriteLine("item 5: {0}", item5.Id);
          var item6 = pool.Get();
          Console.WriteLine("item 6: {0}", item6.Id);

          Console.ReadKey();

          注意示例代碼中我給對象池指定了容量為 2,然后借走 3 個再歸還 3 個,后面再借走 3 個。來看看打印結(jié)果:

          我們看到,item1 與 item4 是同一個對象,item2 與 item5 是同一個對象。item3 與 item6 卻不是同一個對象。

          也就是說,當(dāng)對象從池中取出超過指定容量的對象數(shù)量,雖然歸還了相同數(shù)量的對象,但對象池只允許容納 2 個對象,第三個對象不會被池化。

          4在 ASP.NET Core 中使用

          ASP.NET Core 框架內(nèi)置好了 Microsoft.Extensions.ObjectPool,不需要單獨安裝。官方文檔有個基于 ASP.NET Core 的使用示例:

          https://docs.microsoft.com/en-us/aspnet/core/performance/objectpool

          這個例子把 StringBuilder 做了池化。我這里就直接貼官方的例子了,為了更直觀些,我把無關(guān)的代碼簡化掉了。

          先定義一個中間件:

          public class BirthdayMiddleware
          {
          private readonly RequestDelegate _next;

          public BirthdayMiddleware(RequestDelegate next)
          {
          _next = next;
          }

          public async Task InvokeAsync(HttpContext context, ObjectPool<StringBuilder> builderPool)
          {
          var stringBuilder = builderPool.Get();
          try
          {
          stringBuilder.Append("Hi");
          // 其它處理
          await context.Response.WriteAsync(stringBuilder.ToString());
          }
          finally // 即使出錯也要保證歸還對象
          {
          builderPool.Return(stringBuilder);
          }
          }
          }

          在 Startup 中注冊相應(yīng)的服務(wù)和中間件:

          public class Startup
          {
          public void ConfigureServices(IServiceCollection services)
          {
          services.TryAddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();

          services.TryAddSingleton<ObjectPool<StringBuilder>>(serviceProvider =>
          {
          var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
          var policy = new StringBuilderPooledObjectPolicy();
          return provider.Create(policy);
          });
          }

          public void Configure(IApplicationBuilder app, IHostingEnvironment env)
          {
          app.UseMiddleware<BirthdayMiddleware>();
          }
          }

          這個示例用了 DefaultObjectPoolProvider,它是默認(rèn)的對象池 Provider,所以你也可以自定義自己的對象池 Provider。

          5總結(jié)

          Microsoft.Extensions.ObjectPool 提供的對象池功能還是挺靈活的。普通場景使用使用默認(rèn)的池化策略、默認(rèn)的對象池和默認(rèn)的對象池提供者就可以滿足需求,也可以自定義其中任意某部件來實現(xiàn)比較特殊或復(fù)雜的需求。

          對象池的使用原則是:有借有還,再借不難。當(dāng)調(diào)用組件從對象池中借走一個對象實例,使用完后應(yīng)立即歸還給對象池,以便重復(fù)利用,避免因過多的對象初始化影響系統(tǒng)性能。


          往期精彩回顧




          【推薦】.NET Core開發(fā)實戰(zhàn)視頻課程 ★★★

          .NET Core實戰(zhàn)項目之CMS 第一章 入門篇-開篇及總體規(guī)劃

          【.NET Core微服務(wù)實戰(zhàn)-統(tǒng)一身份認(rèn)證】開篇及目錄索引

          Redis基本使用及百億數(shù)據(jù)量中的使用技巧分享(附視頻地址及觀看指南)

          .NET Core中的一個接口多種實現(xiàn)的依賴注入與動態(tài)選擇看這篇就夠了

          10個小技巧助您寫出高性能的ASP.NET Core代碼

          用abp vNext快速開發(fā)Quartz.NET定時任務(wù)管理界面

          在ASP.NET Core中創(chuàng)建基于Quartz.NET托管服務(wù)輕松實現(xiàn)作業(yè)調(diào)度

          現(xiàn)身說法:實際業(yè)務(wù)出發(fā)分析百億數(shù)據(jù)量下的多表查詢優(yōu)化

          關(guān)于C#異步編程你應(yīng)該了解的幾點建議

          C#異步編程看這篇就夠了


          瀏覽 39
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩欧美一级在线视频 | 日韩V高清 | 20011年高清a免费看一级毛片 | 九九九九九九九九九九九九九九十九 免费 琪琪先锋 torrent magnet | 操逼免费网址 |